Compare commits
396 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
68fd0d0c6a | ||
|
|
a95403849d | ||
|
|
554f8d87bc | ||
|
|
895619d194 | ||
|
|
fd77157f72 | ||
|
|
0d8786db21 | ||
|
|
e9e86c12bd | ||
|
|
c61a3e2f1d | ||
|
|
9f36969e81 | ||
|
|
b8c970cefe | ||
|
|
da1ee461db | ||
|
|
c691a96d26 | ||
|
|
90d2bb3e47 | ||
|
|
2bfaad5500 | ||
|
|
c186ba5e4e | ||
|
|
38378794ea | ||
|
|
841f3424f9 | ||
|
|
81b34ff54a | ||
|
|
9b9db55f49 | ||
|
|
e7c7367ea8 | ||
|
|
5e37ef32fd | ||
|
|
85525745a1 | ||
|
|
c7f581a088 | ||
|
|
a88dfb1bc3 | ||
|
|
410cd8fc1a | ||
|
|
bb30be3c4d | ||
|
|
6230c9e87a | ||
|
|
93efb64211 | ||
|
|
3c4dd91d36 | ||
|
|
45041fdab1 | ||
|
|
bcf0613ff0 | ||
|
|
3107b851c5 | ||
|
|
b27883ad0f | ||
|
|
e0c3502c3f | ||
|
|
4c2db002eb | ||
|
|
898f769d24 | ||
|
|
d6549c6a6b | ||
|
|
eea4081d58 | ||
|
|
1e9f48e48c | ||
|
|
0816933679 | ||
|
|
8af6ff2bd7 | ||
|
|
c5d979fd28 | ||
|
|
beb0c29326 | ||
|
|
5509ace412 | ||
|
|
6f296a1170 | ||
|
|
f8228a71f3 | ||
|
|
9886f04b48 | ||
|
|
37c4aad4b0 | ||
|
|
77d7b28e26 | ||
|
|
3505b22344 | ||
|
|
80cec08982 | ||
|
|
f92094daec | ||
|
|
2c06ef1da6 | ||
|
|
06403ece3f | ||
|
|
39a6491403 | ||
|
|
edb82eb3be | ||
|
|
c7ca3df50d | ||
|
|
0b198381b1 | ||
|
|
2dcda83741 | ||
|
|
5b3c3f0ff2 | ||
|
|
23454f7c7f | ||
|
|
0fdfe29343 | ||
|
|
fa7a799e2b | ||
|
|
9c1a707279 | ||
|
|
7b69bb05d4 | ||
|
|
3b6b6ea11b | ||
|
|
9ec65ab027 | ||
|
|
da5531a947 | ||
|
|
4744f91f6b | ||
|
|
36b80c1d7e | ||
|
|
15f4ab7248 | ||
|
|
0e25c25480 | ||
|
|
7ff48a407d | ||
|
|
bbc371b1c5 | ||
|
|
d141d60e77 | ||
|
|
80af701e5c | ||
|
|
f0e70e03cb | ||
|
|
aba3472f2e | ||
|
|
8772076d76 | ||
|
|
4abe03347a | ||
|
|
b3e6e817dd | ||
|
|
e48445c224 | ||
|
|
159f19d5ec | ||
|
|
c2953a530c | ||
|
|
9166f129d8 | ||
|
|
72ac3eafdd | ||
|
|
8610b41fad | ||
|
|
06e4c0e83e | ||
|
|
5db3fd9f9b | ||
|
|
978afb2b38 | ||
|
|
b00b69a582 | ||
|
|
9a33b0cfb3 | ||
|
|
f4bf42bf07 | ||
|
|
671a6e32d0 | ||
|
|
d05534fb17 | ||
|
|
b655c8c8bc | ||
|
|
8fbea8ab57 | ||
|
|
2ddfc66104 | ||
|
|
26d62cdef4 | ||
|
|
7f6be795b5 | ||
|
|
d7fc97b3d7 | ||
|
|
4a2aa9ee4b | ||
|
|
f4aeec0937 | ||
|
|
ea06a52921 | ||
|
|
26dc24d216 | ||
|
|
1c57c37997 | ||
|
|
15a6370785 | ||
|
|
c8580ff6e6 | ||
|
|
a104e7cfa8 | ||
|
|
c649d97021 | ||
|
|
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 | ||
|
|
fea076318d | ||
|
|
df867fa919 | ||
|
|
a7f0030462 | ||
|
|
b5274c79b0 | ||
|
|
a2de1984e3 | ||
|
|
81b5391f64 | ||
|
|
2c6ad44974 | ||
|
|
e1c942dc7b | ||
|
|
4420e6961e | ||
|
|
2deffab3ba | ||
|
|
05a07bee62 | ||
|
|
025d1606f7 | ||
|
|
b72c88dfb9 | ||
|
|
db240cc163 | ||
|
|
0ef55f0139 | ||
|
|
0ad9b49224 | ||
|
|
ccbf4fb5d7 | ||
|
|
46e376a351 | ||
|
|
23e693a314 | ||
|
|
3a3e37f4bd | ||
|
|
e568053b95 | ||
|
|
fe9cd9f5bc | ||
|
|
8cf3ec94d9 | ||
|
|
112f92d3e0 | ||
|
|
88db766337 | ||
|
|
205b9d7979 | ||
|
|
d5a7771a67 | ||
|
|
655ed7fb16 | ||
|
|
808f3d2012 | ||
|
|
ee4b944abf | ||
|
|
b4abd9369f | ||
|
|
079da637ec | ||
|
|
0c8a100d1c | ||
|
|
0cc330463f | ||
|
|
f50d669a5f | ||
|
|
57fd1d325e | ||
|
|
e31b921486 | ||
|
|
08b39a5bb1 | ||
|
|
1fefc792fc | ||
|
|
f134785696 | ||
|
|
b9a4784de2 | ||
|
|
855472fe68 | ||
|
|
4834d4423c | ||
|
|
4de2177cc7 | ||
|
|
ca9f9c9160 | ||
|
|
7c0f688f9d | ||
|
|
4bfae63201 | ||
|
|
419a56518f | ||
|
|
36efdcd869 | ||
|
|
a297aca812 | ||
|
|
0b4dd2e859 | ||
|
|
3142306a0c | ||
|
|
9d4765c1ea | ||
|
|
547dffb922 | ||
|
|
84e61acde7 | ||
|
|
11c756466c | ||
|
|
06291ffc4b | ||
|
|
1f65e5ba0a | ||
|
|
392f0fd326 | ||
|
|
a6b6407679 | ||
|
|
96678bf5df | ||
|
|
3c37b8286f | ||
|
|
38862f6ad8 | ||
|
|
35213238f4 | ||
|
|
2a03388d6b | ||
|
|
ef704a9d45 | ||
|
|
946978fa86 | ||
|
|
70b03e67fb | ||
|
|
1ee7c6f032 | ||
|
|
f2f58fb888 | ||
|
|
5286568801 | ||
|
|
2a9f8ed0dc | ||
|
|
eb3d064cb5 | ||
|
|
a0ec282cc2 | ||
|
|
ea61dca27b | ||
|
|
fd0be9e96b | ||
|
|
775247e28b | ||
|
|
ded5c39790 | ||
|
|
16e44d71bd | ||
|
|
ae0858319f | ||
|
|
3c6733648b | ||
|
|
06bd915964 | ||
|
|
dfcd786f53 | ||
|
|
d0f6282b81 | ||
|
|
ab3a6e1fd2 | ||
|
|
0cb540d3e5 | ||
|
|
1d0123d69f | ||
|
|
6dbcb8e621 | ||
|
|
b1a6d58974 | ||
|
|
bb45006ff7 | ||
|
|
e9f81faf70 | ||
|
|
b864c6ca15 | ||
|
|
9f98231eed | ||
|
|
3bcf5b1618 | ||
|
|
d7a6245f8a | ||
|
|
2abf2aeda7 | ||
|
|
c3129c7cee | ||
|
|
3b0ec94d2b | ||
|
|
9902d98342 | ||
|
|
b5f5baeb8f | ||
|
|
f396d5ea05 | ||
|
|
e616aa5e2a | ||
|
|
f041093fe9 | ||
|
|
e66c056283 | ||
|
|
d33bbcff06 | ||
|
|
992c2f152f | ||
|
|
118bc5058c | ||
|
|
19c11f2694 | ||
|
|
0a812ff6c4 | ||
|
|
1e6a98df88 | ||
|
|
b1b4e61244 | ||
|
|
2a05b2abdb | ||
|
|
74beb47ad5 | ||
|
|
325a7f3b0d | ||
|
|
9b5b5a1334 | ||
|
|
74dee7a892 | ||
|
|
d07436d876 | ||
|
|
e283152a89 | ||
|
|
bdeeccedb9 | ||
|
|
5931266770 | ||
|
|
bbeb75631d | ||
|
|
c0f4940289 | ||
|
|
db1b81243a | ||
|
|
97b6a8259f | ||
|
|
4c92ff9e70 | ||
|
|
b8dc7f4526 | ||
|
|
0d6e0d6e9f | ||
|
|
1a5bac68ad | ||
|
|
83e302aeb7 | ||
|
|
2196eaa68c | ||
|
|
40aae02af6 | ||
|
|
f078df6f8e | ||
|
|
ee44bfe0a8 | ||
|
|
9d9a808226 | ||
|
|
d78784c0d9 | ||
|
|
1ead8a5594 | ||
|
|
b63c8f4daf | ||
|
|
011400f714 | ||
|
|
1d725579de | ||
|
|
93749c3841 | ||
|
|
4b0c67b8fa | ||
|
|
165040872c | ||
|
|
27773be1c7 | ||
|
|
fadb50a28b | ||
|
|
518a9c3492 | ||
|
|
d5eb4dc49f | ||
|
|
e76bee574f | ||
|
|
b7779a3e6f | ||
|
|
4cb5b48985 | ||
|
|
b2128113ba | ||
|
|
58d0ae6da1 | ||
|
|
2bd821f59d | ||
|
|
8d002225fd | ||
|
|
0243c2f0fe | ||
|
|
b8edd7f440 | ||
|
|
4683aecf7b | ||
|
|
16d38ba2b4 | ||
|
|
fe7fe06d48 | ||
|
|
c1220ef752 | ||
|
|
f0c221a863 | ||
|
|
1df6726a3b | ||
|
|
1a9e939f9c | ||
|
|
39cb188604 | ||
|
|
aba4d12302 | ||
|
|
17c77fd48a | ||
|
|
1cfc12ec19 | ||
|
|
b0aa1dc28a | ||
|
|
0c12b394a7 | ||
|
|
9e468bc82e | ||
|
|
973a15b593 | ||
|
|
253c1e7723 | ||
|
|
7b44051190 | ||
|
|
a25ac77ebc | ||
|
|
d187167fbe | ||
|
|
b34347cf43 | ||
|
|
829d9447a4 | ||
|
|
39067c6a35 | ||
|
|
6dbb64a4f2 | ||
|
|
f8041290d2 | ||
|
|
f772ac2e9d | ||
|
|
ef485ef64a | ||
|
|
b164d5e83e |
@@ -12,6 +12,7 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
rules: {
|
rules: {
|
||||||
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
||||||
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off'
|
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
||||||
|
'standard/no-callback-literal': 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
4
.github/ISSUE_TEMPLATE/bug.md
vendored
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
name: 报告Bug(请先查看常见问题及搜索issue列表中有无你要提的问题)
|
name: 报告Bug(请先查看常见问题及搜索已关闭issue列表中有无你要提的问题)
|
||||||
about: 创建报告以帮助我们改进
|
about: 创建报告以帮助我们改进
|
||||||
title: '(未回答的问题请删除)'
|
title: '(未回答的问题请删除,减少多余信息干扰)'
|
||||||
labels: bug
|
labels: bug
|
||||||
assignees: ''
|
assignees: ''
|
||||||
|
|
||||||
|
|||||||
10
.github/workflows/x86.yml
vendored
@@ -1,4 +1,4 @@
|
|||||||
name: release-build
|
name: x86-release-build
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
@@ -25,11 +25,3 @@ jobs:
|
|||||||
shell: pwsh
|
shell: pwsh
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Upload artifact
|
|
||||||
uses: actions/upload-artifact@v2
|
|
||||||
with:
|
|
||||||
name: dist_electron
|
|
||||||
path: dist_electron/*.exe
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|||||||
25
README.md
@@ -42,9 +42,19 @@
|
|||||||
|
|
||||||
### 🌴 下载
|
### 🌴 下载
|
||||||
|
|
||||||
- 🍓 [Github -- 官方下载](https://github.com/Hunlongyu/ZY-Player/releases)
|
- 🎃 软件暂时关闭下载通道. 请大家支持正版.
|
||||||
- 🍉 [蓝奏云 -- 快速下载](https://www.lanzoux.com/b04s6a3re) 密码:95px
|
- 🎭 所有资源来自网上, 该软件不参与任何制作, 上传, 储存等内容, 禁止传播违法资源. 该软件仅供学习参考, 请于安装后24小时内删除.
|
||||||
- 🍒 适用于32位操作系统的x86软件,在蓝奏云网盘里, 后缀名: ZY Player * 32位.exe
|
|
||||||
|
### 🎠 平台
|
||||||
|
|
||||||
|
| 平台 | 链接 |
|
||||||
|
| :------------------------------------ | :---------------------------------------------------------- |
|
||||||
|
| 🖥️ 电脑端 ( Windows & Mac & Linux ) | [ZY Player](https://github.com/Hunlongyu/ZY-Player) |
|
||||||
|
| 📱 手机端 ( Android & IOS ) | [ZY Player APP](https://github.com/Hunlongyu/ZY-Player-APP) |
|
||||||
|
| 📺 电视端 ( Android & Mac ) ( 进行中 ) | [ZY Player TV](https://github.com/cuiocean/ZY-Player-TV) |
|
||||||
|
| 🌐 浏览器 ( Web ) | [ZY Player Web](https://github.com/Hunlongyu/ZY-Player-Web) |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### 🚀 快捷键
|
### 🚀 快捷键
|
||||||
|
|
||||||
@@ -81,7 +91,8 @@
|
|||||||
|
|
||||||
### 🍭 开发者
|
### 🍭 开发者
|
||||||
|
|
||||||
| [Hunlongyu](https://github.com/Hunlongyu) | [cuiocean](https://github.com/cuiocean) |
|
| [Hunlongyu](https://github.com/Hunlongyu) | [cuiocean](https://github.com/cuiocean) | [buvta](https://github.com/buvta) |
|
||||||
| :----------------------------------------------------------: | :----------------------------------------------------------: |
|
| :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: |
|
||||||
| <img width="120" src="https://avatars2.githubusercontent.com/u/15273630?s=460&u=48cf3299e2a842c0252233d8be42ef4c5d792138&v=4"/> | <img width="120" src="https://avatars0.githubusercontent.com/u/5760235?s=460&u=9d969dd8d83f069ce7ebd60516770c93ac07a330&v=4" /> |
|
| <img width="120" src="https://avatars2.githubusercontent.com/u/15273630?s=460&u=48cf3299e2a842c0252233d8be42ef4c5d792138&v=4"/> | <img width="120" src="https://avatars0.githubusercontent.com/u/5760235?s=460&u=9d969dd8d83f069ce7ebd60516770c93ac07a330&v=4" /> | <img width="120" src="https://avatars3.githubusercontent.com/u/12312540?s=400&v=4" /> |
|
||||||
| 💻 🎨 🐛 | 💻 🐛 |
|
| 💻 🎨 🐛 | 💻 🐛 | 💻 🐛 |
|
||||||
|
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 68 KiB |
|
Before Width: | Height: | Size: 166 KiB |
|
Before Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 73 KiB |
|
Before Width: | Height: | Size: 61 KiB |
|
Before Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 224 KiB |
BIN
docs/assets/img/gallery/01.png
Normal file
|
After Width: | Height: | Size: 944 KiB |
BIN
docs/assets/img/gallery/02.png
Normal file
|
After Width: | Height: | Size: 927 KiB |
BIN
docs/assets/img/gallery/03.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
docs/assets/img/gallery/04.png
Normal file
|
After Width: | Height: | Size: 691 KiB |
BIN
docs/assets/img/gallery/05.png
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
docs/assets/img/gallery/06.png
Normal file
|
After Width: | Height: | Size: 152 KiB |
BIN
docs/assets/img/gallery/07.png
Normal file
|
After Width: | Height: | Size: 36 KiB |
BIN
docs/assets/img/gallery/08.png
Normal file
|
After Width: | Height: | Size: 206 KiB |
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 1.1 MiB |
@@ -70,7 +70,7 @@
|
|||||||
<h1>ZY Player 资源播放器</h1>
|
<h1>ZY Player 资源播放器</h1>
|
||||||
<h2>跨平台视频资源播放器, 简洁免费无广告.</h2>
|
<h2>跨平台视频资源播放器, 简洁免费无广告.</h2>
|
||||||
<a href="https://github.com/Hunlongyu/ZY-Player/releases" target="_blank" class="download-btn"><i class="icofont-home"></i></i> Github 下载</a>
|
<a href="https://github.com/Hunlongyu/ZY-Player/releases" target="_blank" class="download-btn"><i class="icofont-home"></i></i> Github 下载</a>
|
||||||
<a href="https://www.lanzous.com/b04s6a3re" target="_blank" class="download-btn"><i class="icofont-cloud"></i> 蓝奏下载 (密码:95px)</a>
|
<a href="https://hly.lanzoul.com/b04s6a3re" target="_blank" class="download-btn"><i class="icofont-cloud"></i> 蓝奏下载 (密码:95px)</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-6 d-lg-flex flex-lg-column align-items-stretch order-1 order-lg-2 hero-img" data-aos="fade-up">
|
<div class="col-lg-6 d-lg-flex flex-lg-column align-items-stretch order-1 order-lg-2 hero-img" data-aos="fade-up">
|
||||||
@@ -89,7 +89,7 @@
|
|||||||
|
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h2>软件特色</h2>
|
<h2>软件特色</h2>
|
||||||
<p>经过三个大版本更迭, 软件功能丰富, 操作简单.</p>
|
<p>软件功能丰富, 操作简单.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row no-gutters">
|
<div class="row no-gutters">
|
||||||
@@ -99,7 +99,7 @@
|
|||||||
<div class="col-md-6 icon-box" data-aos="fade-up">
|
<div class="col-md-6 icon-box" data-aos="fade-up">
|
||||||
<i class="bx bx-receipt"></i>
|
<i class="bx bx-receipt"></i>
|
||||||
<h4>浏览</h4>
|
<h4>浏览</h4>
|
||||||
<p>浏览全网热门视频, 支持切换视频源. 详细的电影分类.支持搜索电影名和演员名称. </p>
|
<p>浏览全网热门视频, 支持切换视频源. 详细的电影分类. </p>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6 icon-box" data-aos="fade-up" data-aos-delay="100">
|
<div class="col-md-6 icon-box" data-aos="fade-up" data-aos-delay="100">
|
||||||
<i class="icofont-play-alt-3"></i>
|
<i class="icofont-play-alt-3"></i>
|
||||||
@@ -124,7 +124,7 @@
|
|||||||
<div class="col-md-6 icon-box" data-aos="fade-up" data-aos-delay="500">
|
<div class="col-md-6 icon-box" data-aos="fade-up" data-aos-delay="500">
|
||||||
<i class="icofont-cubes"></i>
|
<i class="icofont-cubes"></i>
|
||||||
<h4>其他</h4>
|
<h4>其他</h4>
|
||||||
<p>多主题, 多语言, 自动更新</p>
|
<p>多主题, 自动更新</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -146,14 +146,14 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="owl-carousel gallery-carousel" data-aos="fade-up">
|
<div class="owl-carousel gallery-carousel" data-aos="fade-up">
|
||||||
<a href="assets/img/gallery/001.png" class="venobox" data-gall="gallery-carousel"><img src="assets/img/gallery/001.png" alt=""></a>
|
<a href="assets/img/gallery/01.png" class="venobox" data-gall="gallery-carousel"><img src="assets/img/gallery/01.png" alt=""></a>
|
||||||
<a href="assets/img/gallery/002.png" class="venobox" data-gall="gallery-carousel"><img src="assets/img/gallery/002.png" alt=""></a>
|
<a href="assets/img/gallery/02.png" class="venobox" data-gall="gallery-carousel"><img src="assets/img/gallery/02.png" alt=""></a>
|
||||||
<a href="assets/img/gallery/003.png" class="venobox" data-gall="gallery-carousel"><img src="assets/img/gallery/003.png" alt=""></a>
|
<a href="assets/img/gallery/03.png" class="venobox" data-gall="gallery-carousel"><img src="assets/img/gallery/03.png" alt=""></a>
|
||||||
<a href="assets/img/gallery/004.png" class="venobox" data-gall="gallery-carousel"><img src="assets/img/gallery/004.png" alt=""></a>
|
<a href="assets/img/gallery/04.png" class="venobox" data-gall="gallery-carousel"><img src="assets/img/gallery/04.png" alt=""></a>
|
||||||
<a href="assets/img/gallery/005.png" class="venobox" data-gall="gallery-carousel"><img src="assets/img/gallery/005.png" alt=""></a>
|
<a href="assets/img/gallery/05.png" class="venobox" data-gall="gallery-carousel"><img src="assets/img/gallery/05.png" alt=""></a>
|
||||||
<a href="assets/img/gallery/006.png" class="venobox" data-gall="gallery-carousel"><img src="assets/img/gallery/006.png" alt=""></a>
|
<a href="assets/img/gallery/06.png" class="venobox" data-gall="gallery-carousel"><img src="assets/img/gallery/06.png" alt=""></a>
|
||||||
<a href="assets/img/gallery/007.png" class="venobox" data-gall="gallery-carousel"><img src="assets/img/gallery/007.png" alt=""></a>
|
<a href="assets/img/gallery/07.png" class="venobox" data-gall="gallery-carousel"><img src="assets/img/gallery/07.png" alt=""></a>
|
||||||
<a href="assets/img/gallery/008.png" class="venobox" data-gall="gallery-carousel"><img src="assets/img/gallery/008.png" alt=""></a>
|
<a href="assets/img/gallery/08.png" class="venobox" data-gall="gallery-carousel"><img src="assets/img/gallery/08.png" alt=""></a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@@ -193,7 +193,7 @@
|
|||||||
<i class="bx bx-help-circle icon-help"></i> <a data-toggle="collapse" href="#accordion-list-3" class="collapsed">跨平台<i class="bx bx-chevron-down icon-show"></i><i class="bx bx-chevron-up icon-close"></i></a>
|
<i class="bx bx-help-circle icon-help"></i> <a data-toggle="collapse" href="#accordion-list-3" class="collapsed">跨平台<i class="bx bx-chevron-down icon-show"></i><i class="bx bx-chevron-up icon-close"></i></a>
|
||||||
<div id="accordion-list-3" class="collapse" data-parent=".accordion-list">
|
<div id="accordion-list-3" class="collapse" data-parent=".accordion-list">
|
||||||
<p>
|
<p>
|
||||||
目前支持 Windows, Mac, Linux 桌面系统. 暂不支持手机端或者电视端. 未来会考虑实现全平台.
|
目前支持 Windows, Mac, Linux, Android, IOS, TV, Web 全平台.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
@@ -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="#">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.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://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>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
34209
package-lock.json
generated
Normal file
62
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "zy",
|
"name": "zy",
|
||||||
"version": "2.6.8",
|
"version": "2.8.8",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"serve": "vue-cli-service serve",
|
"serve": "vue-cli-service serve",
|
||||||
@@ -17,58 +17,50 @@
|
|||||||
},
|
},
|
||||||
"main": "background.js",
|
"main": "background.js",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^0.21.0",
|
"@electron/remote": "^2.0.8",
|
||||||
"cheerio": "^1.0.0-rc.3",
|
"axios": "^0.21.2",
|
||||||
"core-js": "^3.8.0",
|
"bootstrap-vue": "^2.22.0",
|
||||||
"cors": "^2.8.5",
|
"cheerio": "1.0.0-rc.6",
|
||||||
"dexie": "^3.0.3",
|
"core-js": "^3.10.2",
|
||||||
|
"dexie": "^3.2.2",
|
||||||
"electron-localshortcut": "^3.2.1",
|
"electron-localshortcut": "^3.2.1",
|
||||||
"electron-proxy-agent": "^1.2.0",
|
"electron-proxy-agent": "^1.2.0",
|
||||||
"electron-updater": "^4.3.5",
|
"electron-updater": "^4.3.8",
|
||||||
"element-ui": "^2.14.1",
|
"element-ui": "^2.15.9",
|
||||||
"express": "^4.17.1",
|
"fast-xml-parser": "^3.19.0",
|
||||||
"fast-xml-parser": "^3.17.4",
|
|
||||||
"html2canvas": "^1.0.0-rc.7",
|
"html2canvas": "^1.0.0-rc.7",
|
||||||
"iptv-playlist-parser": "^0.5.0",
|
"iptv-playlist-parser": "^0.6.0",
|
||||||
"m3u": "0.0.2",
|
"m3u": "0.0.2",
|
||||||
"m3u8-parser": "^4.5.0",
|
"m3u8-parser": "^4.6.0",
|
||||||
"memcached": "^2.2.2",
|
"memcached": "^2.2.2",
|
||||||
"modern-normalize": "^1.0.0",
|
"modern-normalize": "^1.0.0",
|
||||||
"mousetrap": "^1.6.5",
|
"mousetrap": "^1.6.5",
|
||||||
"pinyin-match": "^1.1.1",
|
"pinyin-match": "^1.2.1",
|
||||||
"qrcode.vue": "^1.7.0",
|
"qrcode.vue": "^1.7.0",
|
||||||
"randomstring": "^1.1.5",
|
"randomstring": "^1.1.5",
|
||||||
"session": "^0.1.0",
|
"session": "^0.1.0",
|
||||||
"sortablejs": "^1.12.0",
|
"sortablejs": "^1.13.0",
|
||||||
"v-fit-columns": "^0.2.0",
|
"v-fit-columns": "^0.2.0",
|
||||||
"vue": "^2.6.12",
|
"vue": "^2.6.14",
|
||||||
"vue-clickaway": "^2.2.2",
|
|
||||||
"vue-infinite-loading": "^2.4.5",
|
"vue-infinite-loading": "^2.4.5",
|
||||||
"vue-waterfall-plugin": "^1.1.0",
|
"vue-waterfall-plugin": "^1.1.0",
|
||||||
"vuedraggable": "^2.24.3",
|
"vuedraggable": "^2.24.3",
|
||||||
"vuex": "^3.6.0",
|
"vuex": "^3.6.2",
|
||||||
"xgplayer": "^2.13.1",
|
"xgplayer": "2.19.1",
|
||||||
"xgplayer-hls.js": "^2.2.5"
|
"xgplayer-flv.js": "^2.3.0",
|
||||||
|
"xgplayer-hls.js": "^2.4.2",
|
||||||
|
"xgplayer-mp4": "^1.2.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vue/cli-plugin-babel": "~4.5.9",
|
"@vue/cli-plugin-babel": "~5.0.8",
|
||||||
"@vue/cli-plugin-eslint": "~4.5.9",
|
"@vue/cli-plugin-vuex": "~5.0.8",
|
||||||
"@vue/cli-plugin-vuex": "~4.5.9",
|
"@vue/cli-service": "~5.0.8",
|
||||||
"@vue/cli-service": "~4.5.9",
|
|
||||||
"@vue/eslint-config-standard": "^5.1.2",
|
|
||||||
"babel-eslint": "^10.1.0",
|
|
||||||
"babel-plugin-component": "^1.1.1",
|
"babel-plugin-component": "^1.1.1",
|
||||||
"electron": "^11.0.3",
|
"electron": "^13.0.0",
|
||||||
"electron-devtools-installer": "^3.1.1",
|
"electron-devtools-installer": "^3.1.1",
|
||||||
"eslint": "^7.14.0",
|
"sass": "^1.30.0",
|
||||||
"eslint-plugin-import": "^2.22.1",
|
|
||||||
"eslint-plugin-node": "^11.1.0",
|
|
||||||
"eslint-plugin-promise": "^4.2.1",
|
|
||||||
"eslint-plugin-standard": "^4.1.0",
|
|
||||||
"eslint-plugin-vue": "^7.1.0",
|
|
||||||
"sass": "^1.29.0",
|
|
||||||
"sass-loader": "^10.1.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"
|
"vue-template-compiler": "^2.6.14"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||||
|
<meta name="referrer" content="same-origin"/>
|
||||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||||
<title><%= htmlWebpackPlugin.options.title %></title>
|
<title><%= htmlWebpackPlugin.options.title %></title>
|
||||||
</head>
|
</head>
|
||||||
|
|||||||
54
src/App.vue
@@ -10,7 +10,6 @@
|
|||||||
<Setting v-show="view === 'Setting'" />
|
<Setting v-show="view === 'Setting'" />
|
||||||
<IPTV v-show="view === 'IPTV'" />
|
<IPTV v-show="view === 'IPTV'" />
|
||||||
<EditSites v-if="view === 'EditSites'"/>
|
<EditSites v-if="view === 'EditSites'"/>
|
||||||
<Recommendation v-show="view === 'Recommendation'" />
|
|
||||||
</div>
|
</div>
|
||||||
<transition name="slide">
|
<transition name="slide">
|
||||||
<Detail v-if="detail.show"/>
|
<Detail v-if="detail.show"/>
|
||||||
@@ -22,11 +21,62 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import { setting } from './lib/dexie'
|
||||||
|
const remote = require('@electron/remote')
|
||||||
export default {
|
export default {
|
||||||
name: 'App',
|
name: 'App',
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
appTheme: 'theme-light'
|
appTheme: 'theme-light',
|
||||||
|
winSizePosition: {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
width: 0,
|
||||||
|
height: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
// 窗口创建口,检查是否有窗口大小位置的记录,如果有的话,更新窗口位置及大小
|
||||||
|
setting.find().then(res => {
|
||||||
|
if (res.restoreWindowPositionAndSize) {
|
||||||
|
var win = remote.getCurrentWindow()
|
||||||
|
win.setBounds({
|
||||||
|
x: res.windowPositionAndSize.x,
|
||||||
|
y: res.windowPositionAndSize.y,
|
||||||
|
width: res.windowPositionAndSize.width,
|
||||||
|
height: res.windowPositionAndSize.height
|
||||||
|
})
|
||||||
|
this.winSizePosition = {
|
||||||
|
x: win.getPosition()[0],
|
||||||
|
y: win.getPosition()[1],
|
||||||
|
width: win.getSize()[0],
|
||||||
|
height: win.getSize()[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
updated () {
|
||||||
|
// 本来想hook up到beforedestroy, 但不工作
|
||||||
|
// 每当窗口更新时,检查窗口大小及位置,记录到setting数据库中
|
||||||
|
if (this.setting.restoreWindowPositionAndSize) {
|
||||||
|
const win = remote.getCurrentWindow()
|
||||||
|
var newWinSizePosition = {
|
||||||
|
x: win.getPosition()[0],
|
||||||
|
y: win.getPosition()[1],
|
||||||
|
width: win.getSize()[0],
|
||||||
|
height: win.getSize()[1]
|
||||||
|
}
|
||||||
|
if (newWinSizePosition.x !== this.winSizePosition.x ||
|
||||||
|
newWinSizePosition.y !== this.winSizePosition.y ||
|
||||||
|
newWinSizePosition.width !== this.winSizePosition.width ||
|
||||||
|
newWinSizePosition.height !== this.winSizePosition.height) {
|
||||||
|
this.winSizePosition = newWinSizePosition
|
||||||
|
setting.find().then(res => {
|
||||||
|
res.windowPositionAndSize = newWinSizePosition
|
||||||
|
setting.update(res)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
|||||||
@@ -123,7 +123,7 @@
|
|||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
.listpage-header{
|
.listpage-header, .toolbar{
|
||||||
height: 60px;
|
height: 60px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -175,7 +175,38 @@
|
|||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
|
.el-select-dropdown__item.selected.hover{ //是上游的bug吗?临时性修补
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
.el-select-dropdown__wrap{
|
||||||
|
max-height: 574px
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
> span{
|
||||||
|
.el-input-number{
|
||||||
|
width:120px;
|
||||||
|
.el-input{
|
||||||
|
width: 100px;
|
||||||
|
}
|
||||||
|
.el-input__inner{
|
||||||
|
padding-left: 5px;
|
||||||
|
padding-right: 5px;
|
||||||
|
width: 88px;
|
||||||
|
}
|
||||||
|
.el-input-number__increase, .el-input-number__decrease {
|
||||||
|
background-color: inherit;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.el-divider{
|
||||||
|
.el-divider--horizontal{
|
||||||
|
margin: 12px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.toolbar{
|
||||||
|
z-index: 5;
|
||||||
}
|
}
|
||||||
.listpage-body{
|
.listpage-body{
|
||||||
height: calc(100% - 60px);
|
height: calc(100% - 60px);
|
||||||
@@ -252,7 +283,8 @@
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: 0.2s;
|
transition: 0.2s;
|
||||||
&:hover {
|
&:hover {
|
||||||
top: -3px;
|
width: 102%;
|
||||||
|
height: 102%
|
||||||
}
|
}
|
||||||
.img{
|
.img{
|
||||||
position: relative;
|
position: relative;
|
||||||
@@ -409,3 +441,134 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// detail
|
||||||
|
.detail{
|
||||||
|
.detail-content{
|
||||||
|
.detail-body{
|
||||||
|
.m3u8{
|
||||||
|
.show-picture{
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
padding: 10px;
|
||||||
|
.card{
|
||||||
|
border-radius: 6px;
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: 0.2s;
|
||||||
|
&:hover {
|
||||||
|
width: 102%;
|
||||||
|
height: 102%
|
||||||
|
}
|
||||||
|
.img{
|
||||||
|
position: relative;
|
||||||
|
min-height: 40px;
|
||||||
|
img{
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.rate{
|
||||||
|
position: absolute;
|
||||||
|
top: 3%;
|
||||||
|
right: -40%;
|
||||||
|
width: 100%;
|
||||||
|
background-color: #111111aa;
|
||||||
|
color:#2f90b9;
|
||||||
|
height: 30px;
|
||||||
|
line-height: 30px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: bolder;
|
||||||
|
text-align: center;
|
||||||
|
transform: rotate(45deg);
|
||||||
|
}
|
||||||
|
.site{
|
||||||
|
position: absolute;
|
||||||
|
top: 0%;
|
||||||
|
left: 0%;
|
||||||
|
width: 100%;
|
||||||
|
background-color: #111111aa;
|
||||||
|
color:#2f90b9;
|
||||||
|
height: 30px;
|
||||||
|
line-height: 30px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: bolder;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.progress{
|
||||||
|
position: absolute;
|
||||||
|
bottom: 10%;
|
||||||
|
left: 0%;
|
||||||
|
width: 40%;
|
||||||
|
background-color: #111111aa;
|
||||||
|
color: #f8df70;
|
||||||
|
height: 30px;
|
||||||
|
line-height: 30px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: bolder;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
.update{
|
||||||
|
position: absolute;
|
||||||
|
top: 5%;
|
||||||
|
left: -40%;
|
||||||
|
width: 100%;
|
||||||
|
background-color: #68b88e;
|
||||||
|
color: #cdcdcd;
|
||||||
|
height: 30px;
|
||||||
|
line-height: 30px;
|
||||||
|
font-size: 14px;
|
||||||
|
text-align: center;
|
||||||
|
transform: rotate(-45deg);
|
||||||
|
}
|
||||||
|
.operate{
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background-color: #111111aa;
|
||||||
|
width: 100%;
|
||||||
|
font-size: 13px;
|
||||||
|
.operate-wrap{
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
.o-play, .o-star, .o-share{
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-block;
|
||||||
|
width: 80px;
|
||||||
|
height: 36px;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 36px;
|
||||||
|
color: #cdcdcd;
|
||||||
|
&:hover{
|
||||||
|
background-color: #111;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.name{
|
||||||
|
font-size: 16px;
|
||||||
|
padding: 10px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.info{
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
font-size: 12px;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
&:hover{
|
||||||
|
.operate{
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,6 +14,7 @@
|
|||||||
--l-fc-3: #823aa0;
|
--l-fc-3: #823aa0;
|
||||||
--l-bgc-1: #ffffff;
|
--l-bgc-1: #ffffff;
|
||||||
--l-bgc-2: #f2f6f9;
|
--l-bgc-2: #f2f6f9;
|
||||||
|
--l-bgc-3: #F9FBFC;
|
||||||
--l-bsc: 0 1px 3px #8e8da233, 0 1px 2px #8e8da244;
|
--l-bsc: 0 1px 3px #8e8da233, 0 1px 2px #8e8da244;
|
||||||
--l-bsc-hover: 0 14px 28px #8e8da255, 0 10px 10px #8e8da244;
|
--l-bsc-hover: 0 14px 28px #8e8da255, 0 10px 10px #8e8da244;
|
||||||
--l-bsc-2: 0 -4px 23px 0 #8e8da233;
|
--l-bsc-2: 0 -4px 23px 0 #8e8da233;
|
||||||
@@ -32,6 +33,7 @@
|
|||||||
--d-fc-3: #38dd77;
|
--d-fc-3: #38dd77;
|
||||||
--d-bgc-1: #222222;
|
--d-bgc-1: #222222;
|
||||||
--d-bgc-2: #2f2f2f;
|
--d-bgc-2: #2f2f2f;
|
||||||
|
--d-bgc-3: #292929;
|
||||||
--d-bsc: 0 1px 3px #38dd7733, 0 1px 2px #38dd7744;
|
--d-bsc: 0 1px 3px #38dd7733, 0 1px 2px #38dd7744;
|
||||||
--d-bsc-hover: 0 14px 28px #38dd7755, 0 10px 10px #38dd7744;
|
--d-bsc-hover: 0 14px 28px #38dd7755, 0 10px 10px #38dd7744;
|
||||||
--d-bsc-2: 0 -4px 23px 0 #38dd7733;
|
--d-bsc-2: 0 -4px 23px 0 #38dd7733;
|
||||||
@@ -50,6 +52,7 @@
|
|||||||
--g-fc-3: #C1D95C;
|
--g-fc-3: #C1D95C;
|
||||||
--g-bgc-1: #4baea0;
|
--g-bgc-1: #4baea0;
|
||||||
--g-bgc-2: #74b4ac;
|
--g-bgc-2: #74b4ac;
|
||||||
|
--g-bgc-3: #60B1A6;
|
||||||
--g-bsc: 0 1px 3px #e1ebe033, 0 1px 2px #e1ebe044;
|
--g-bsc: 0 1px 3px #e1ebe033, 0 1px 2px #e1ebe044;
|
||||||
--g-bsc-hover: 0 14px 28px #e1ebe055, 0 10px 10px #e1ebe044;
|
--g-bsc-hover: 0 14px 28px #e1ebe055, 0 10px 10px #e1ebe044;
|
||||||
--g-bsc-2: 0 -4px 23px 0 #e1ebe033;
|
--g-bsc-2: 0 -4px 23px 0 #e1ebe033;
|
||||||
@@ -68,6 +71,7 @@
|
|||||||
--p-fc-3: #177ea7;
|
--p-fc-3: #177ea7;
|
||||||
--p-bgc-1: #ff8499;
|
--p-bgc-1: #ff8499;
|
||||||
--p-bgc-2: #fea1b2;
|
--p-bgc-2: #fea1b2;
|
||||||
|
--p-bgc-3: #FF93A6;
|
||||||
--p-bsc: 0 1px 3px #ef528533, 0 1px 2px #ef528544;
|
--p-bsc: 0 1px 3px #ef528533, 0 1px 2px #ef528544;
|
||||||
--p-bsc-hover: 0 14px 28px #ef528555, 0 10px 10px #ef528544;
|
--p-bsc-hover: 0 14px 28px #ef528555, 0 10px 10px #ef528544;
|
||||||
--p-bsc-2: 0 -4px 23px 0 #ef528533;
|
--p-bsc-2: 0 -4px 23px 0 #ef528533;
|
||||||
|
|||||||
@@ -131,6 +131,23 @@
|
|||||||
border-color: var(--d-c-8);
|
border-color: var(--d-c-8);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.selected {
|
||||||
|
background-color: var(--d-c-5);
|
||||||
|
&:hover{
|
||||||
|
background-color: var(--d-c-5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.show-picture{
|
||||||
|
color: var(--d-fc-1);
|
||||||
|
.card{
|
||||||
|
background-color: var(--d-bgc-3);
|
||||||
|
box-shadow: var(--d-bsc);
|
||||||
|
transition: all 0.3s cubic-bezier(.25,.8,.25,1);
|
||||||
|
&:hover{
|
||||||
|
box-shadow: var(--d-bsc-hover);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -139,6 +156,12 @@
|
|||||||
.play{
|
.play{
|
||||||
background-color: var(--d-bgc-1);
|
background-color: var(--d-bgc-1);
|
||||||
box-shadow: var(--d-bsc);
|
box-shadow: var(--d-bsc);
|
||||||
|
.el-switch__label {
|
||||||
|
color: var(--d-fc-2)
|
||||||
|
}
|
||||||
|
.el-switch__label.is-active {
|
||||||
|
color: #409EFF
|
||||||
|
}
|
||||||
.el-input{
|
.el-input{
|
||||||
input{
|
input{
|
||||||
background-color: var(--d-bgc-1);
|
background-color: var(--d-bgc-1);
|
||||||
@@ -155,6 +178,21 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.box{
|
.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{
|
.more{
|
||||||
span{
|
span{
|
||||||
svg{
|
svg{
|
||||||
@@ -319,13 +357,32 @@
|
|||||||
// Page of list using table and picture
|
// Page of list using table and picture
|
||||||
.listpage{
|
.listpage{
|
||||||
color: var(--d-fc-2);
|
color: var(--d-fc-2);
|
||||||
.listpage-header{
|
.listpage-header-divider{
|
||||||
|
background-color: var(--d-bgc-1);
|
||||||
|
.el-divider__text {
|
||||||
|
background-color: var(--d-bgc-2);
|
||||||
|
}
|
||||||
|
.el-button{
|
||||||
|
background-color: var(--d-bgc-2);
|
||||||
|
color: var(--d-fc-2);
|
||||||
|
&:hover{
|
||||||
|
color: var(--d-fc-3)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.listpage-header, .toolbar{
|
||||||
border-bottom-color: var(--d-c-3);
|
border-bottom-color: var(--d-c-3);
|
||||||
.btn{
|
.btn{
|
||||||
&:hover{
|
&:hover{
|
||||||
color: var(--d-fc-3)
|
color: var(--d-fc-3)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.el-switch__label {
|
||||||
|
color: var(--d-fc-2)
|
||||||
|
}
|
||||||
|
.el-switch__label.is-active {
|
||||||
|
color: #409EFF
|
||||||
|
}
|
||||||
.el-button{
|
.el-button{
|
||||||
background-color: var(--d-bgc-2);
|
background-color: var(--d-bgc-2);
|
||||||
color: var(--d-fc-2);
|
color: var(--d-fc-2);
|
||||||
@@ -415,7 +472,7 @@
|
|||||||
.show-picture{
|
.show-picture{
|
||||||
color: var(--d-fc-1);
|
color: var(--d-fc-1);
|
||||||
.card{
|
.card{
|
||||||
background-color: var(--d-bgc-1);
|
background-color: var(--d-bgc-3);
|
||||||
box-shadow: var(--d-bsc);
|
box-shadow: var(--d-bsc);
|
||||||
transition: all 0.3s cubic-bezier(.25,.8,.25,1);
|
transition: all 0.3s cubic-bezier(.25,.8,.25,1);
|
||||||
&:hover{
|
&:hover{
|
||||||
|
|||||||
@@ -131,6 +131,23 @@
|
|||||||
border-color: var(--g-c-8);
|
border-color: var(--g-c-8);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.selected {
|
||||||
|
background-color: var(--g-c-5);
|
||||||
|
&:hover{
|
||||||
|
background-color: var(--g-c-5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.show-picture{
|
||||||
|
color: var(--g-fc-1);
|
||||||
|
.card{
|
||||||
|
background-color: var(--g-bgc-3);
|
||||||
|
box-shadow: var(--g-bsc);
|
||||||
|
transition: all 0.3s cubic-bezier(.25,.8,.25,1);
|
||||||
|
&:hover{
|
||||||
|
box-shadow: var(--g-bsc-hover);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -155,6 +172,21 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.box{
|
.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{
|
.more{
|
||||||
span{
|
span{
|
||||||
svg{
|
svg{
|
||||||
@@ -319,7 +351,20 @@
|
|||||||
// Page of list using table and picture
|
// Page of list using table and picture
|
||||||
.listpage{
|
.listpage{
|
||||||
color: var(--g-fc-2);
|
color: var(--g-fc-2);
|
||||||
.listpage-header{
|
.listpage-header-divider{
|
||||||
|
background-color: var(--g-bgc-1);
|
||||||
|
.el-divider__text {
|
||||||
|
background-color: var(--g-bgc-2);
|
||||||
|
}
|
||||||
|
.el-button{
|
||||||
|
background-color: var(--g-bgc-2);
|
||||||
|
color: var(--g-fc-2);
|
||||||
|
&:hover{
|
||||||
|
color: var(--g-fc-3)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.listpage-header, .toolbar{
|
||||||
border-bottom-color: var(--g-c-3);
|
border-bottom-color: var(--g-c-3);
|
||||||
.btn{
|
.btn{
|
||||||
&:hover{
|
&:hover{
|
||||||
@@ -415,7 +460,7 @@
|
|||||||
.show-picture{
|
.show-picture{
|
||||||
color: var(--g-fc-1);
|
color: var(--g-fc-1);
|
||||||
.card{
|
.card{
|
||||||
background-color: var(--g-bgc-1);
|
background-color: var(--g-bgc-3);
|
||||||
box-shadow: var(--g-bsc);
|
box-shadow: var(--g-bsc);
|
||||||
transition: all 0.3s cubic-bezier(.25,.8,.25,1);
|
transition: all 0.3s cubic-bezier(.25,.8,.25,1);
|
||||||
&:hover{
|
&:hover{
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
.theme-light{
|
.theme-light{
|
||||||
background-color: var(--l-bgc-1);
|
// background-color: var(--l-bgc-1);
|
||||||
|
background: rgba(0, 0, 0, 0);
|
||||||
.zy-select{
|
.zy-select{
|
||||||
color: var(--l-fc-1);
|
color: var(--l-fc-1);
|
||||||
background-color: var(--l-bgc-1);
|
background-color: var(--l-bgc-1);
|
||||||
@@ -131,6 +132,23 @@
|
|||||||
border-color: var(--l-c-8);
|
border-color: var(--l-c-8);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.selected {
|
||||||
|
background-color: var(--l-c-5);
|
||||||
|
&:hover{
|
||||||
|
background-color: var(--l-c-5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.show-picture{
|
||||||
|
color: var(--l-fc-1);
|
||||||
|
.card{
|
||||||
|
background-color: var(--l-bgc-3);
|
||||||
|
box-shadow: var(--l-bsc);
|
||||||
|
transition: all 0.3s cubic-bezier(.25,.8,.25,1);
|
||||||
|
&:hover{
|
||||||
|
box-shadow: var(--l-bsc-hover);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -155,6 +173,21 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.box{
|
.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{
|
.more{
|
||||||
span{
|
span{
|
||||||
svg{
|
svg{
|
||||||
@@ -319,7 +352,20 @@
|
|||||||
// Page of list using table and picture
|
// Page of list using table and picture
|
||||||
.listpage{
|
.listpage{
|
||||||
color: var(--l-fc-2);
|
color: var(--l-fc-2);
|
||||||
.listpage-header{
|
.listpage-header-divider{
|
||||||
|
background-color: var(--l-bgc-1);
|
||||||
|
.el-divider__text {
|
||||||
|
background-color: var(--l-bgc-2);
|
||||||
|
}
|
||||||
|
.el-button{
|
||||||
|
background-color: var(--l-bgc-2);
|
||||||
|
color: var(--l-fc-2);
|
||||||
|
&:hover{
|
||||||
|
color: var(--l-fc-3)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.listpage-header, .toolbar{
|
||||||
border-bottom-color: var(--l-c-3);
|
border-bottom-color: var(--l-c-3);
|
||||||
.btn{
|
.btn{
|
||||||
&:hover{
|
&:hover{
|
||||||
@@ -415,7 +461,7 @@
|
|||||||
.show-picture{
|
.show-picture{
|
||||||
color: var(--l-fc-1);
|
color: var(--l-fc-1);
|
||||||
.card{
|
.card{
|
||||||
background-color: var(--l-bgc-1);
|
background-color: var(--l-bgc-3);
|
||||||
box-shadow: var(--l-bsc);
|
box-shadow: var(--l-bsc);
|
||||||
transition: all 0.3s cubic-bezier(.25,.8,.25,1);
|
transition: all 0.3s cubic-bezier(.25,.8,.25,1);
|
||||||
&:hover{
|
&:hover{
|
||||||
|
|||||||
@@ -131,6 +131,23 @@
|
|||||||
border-color: var(--p-c-8);
|
border-color: var(--p-c-8);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.selected {
|
||||||
|
background-color: var(--p-c-5);
|
||||||
|
&:hover{
|
||||||
|
background-color: var(--p-c-5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.show-picture{
|
||||||
|
color: var(--p-fc-1);
|
||||||
|
.card{
|
||||||
|
background-color: var(--p-bgc-3);
|
||||||
|
box-shadow: var(--p-bsc);
|
||||||
|
transition: all 0.3s cubic-bezier(.25,.8,.25,1);
|
||||||
|
&:hover{
|
||||||
|
box-shadow: var(--p-bsc-hover);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -155,6 +172,21 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.box{
|
.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{
|
.more{
|
||||||
span{
|
span{
|
||||||
svg{
|
svg{
|
||||||
@@ -319,7 +351,20 @@
|
|||||||
// Page of list using table and picture
|
// Page of list using table and picture
|
||||||
.listpage{
|
.listpage{
|
||||||
color: var(--p-fc-2);
|
color: var(--p-fc-2);
|
||||||
.listpage-header{
|
.listpage-header-divider{
|
||||||
|
background-color: var(--p-bgc-1);
|
||||||
|
.el-divider__text {
|
||||||
|
background-color: var(--p-bgc-2);
|
||||||
|
}
|
||||||
|
.el-button{
|
||||||
|
background-color: var(--p-bgc-2);
|
||||||
|
color: var(--p-fc-2);
|
||||||
|
&:hover{
|
||||||
|
color: var(--p-fc-3)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.listpage-header, .toolbar{
|
||||||
border-bottom-color: var(--p-c-3);
|
border-bottom-color: var(--p-c-3);
|
||||||
.btn{
|
.btn{
|
||||||
&:hover{
|
&:hover{
|
||||||
@@ -415,7 +460,7 @@
|
|||||||
.show-picture{
|
.show-picture{
|
||||||
color: var(--p-fc-1);
|
color: var(--p-fc-1);
|
||||||
.card{
|
.card{
|
||||||
background-color: var(--p-bgc-1);
|
background-color: var(--p-bgc-3);
|
||||||
box-shadow: var(--p-bsc);
|
box-shadow: var(--p-bsc);
|
||||||
transition: all 0.3s cubic-bezier(.25,.8,.25,1);
|
transition: all 0.3s cubic-bezier(.25,.8,.25,1);
|
||||||
&:hover{
|
&:hover{
|
||||||
|
|||||||
@@ -1,14 +1,17 @@
|
|||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
import './lib/site/server'
|
|
||||||
import { app, protocol, BrowserWindow, globalShortcut } from 'electron'
|
import { app, protocol, BrowserWindow, globalShortcut } from 'electron'
|
||||||
import { createProtocol } from 'vue-cli-plugin-electron-builder/lib'
|
import { createProtocol } from 'vue-cli-plugin-electron-builder/lib'
|
||||||
import installExtension, { VUEJS_DEVTOOLS } from 'electron-devtools-installer'
|
import installExtension, { VUEJS_DEVTOOLS } from 'electron-devtools-installer'
|
||||||
import { initUpdater } from './lib/update/update'
|
import { initUpdater } from './lib/update/update'
|
||||||
|
require('@electron/remote/main').initialize()
|
||||||
|
|
||||||
const isDevelopment = process.env.NODE_ENV !== 'production'
|
const isDevelopment = process.env.NODE_ENV !== 'production'
|
||||||
|
|
||||||
|
// const log = require('electron-log') // 用于调试主程序
|
||||||
|
|
||||||
app.commandLine.appendSwitch('disable-features', 'OutOfBlinkCors') // 允许跨域
|
app.commandLine.appendSwitch('disable-features', 'OutOfBlinkCors') // 允许跨域
|
||||||
// app.commandLine.appendSwitch('--ignore-certificate-errors', 'true') // 忽略证书相关错误
|
app.commandLine.appendSwitch('--ignore-certificate-errors', 'true') // 忽略证书相关错误
|
||||||
|
|
||||||
let win
|
let win
|
||||||
|
|
||||||
@@ -23,7 +26,9 @@ function createWindow () {
|
|||||||
webPreferences: {
|
webPreferences: {
|
||||||
webSecurity: false,
|
webSecurity: false,
|
||||||
enableRemoteModule: true,
|
enableRemoteModule: true,
|
||||||
nodeIntegration: process.env.ELECTRON_NODE_INTEGRATION
|
nodeIntegration: true,
|
||||||
|
contextIsolation: false,
|
||||||
|
allowRunningInsecureContent: false
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -35,6 +40,24 @@ function createWindow () {
|
|||||||
win.loadURL('app://./index.html')
|
win.loadURL('app://./index.html')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 修改request headers
|
||||||
|
// Sec-Fetch下禁止修改,浏览器自动加上请求头 https://www.cnblogs.com/fulu/p/13879080.html 暂时先用index.html的meta referer policy替代
|
||||||
|
const filter = {
|
||||||
|
urls: ['http://*/*', 'http://*/*']
|
||||||
|
}
|
||||||
|
require("@electron/remote/main").enable(win.webContents)
|
||||||
|
win.webContents.session.webRequest.onBeforeSendHeaders(filter, (details, callback) => {
|
||||||
|
const url = new URL(details.url)
|
||||||
|
details.requestHeaders.Origin = url.origin
|
||||||
|
if (!details.url.includes('//localhost') && details.requestHeaders.Referer && details.requestHeaders.Referer.includes('//localhost')) {
|
||||||
|
details.requestHeaders.Referer = url.origin
|
||||||
|
}
|
||||||
|
callback({ // https://github.com/electron/electron/issues/23988 回调似乎无法修改headers,暂时先用index.html的meta referer policy替代
|
||||||
|
cancel: false,
|
||||||
|
requestHeaders: details.requestHeaders
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
initUpdater(win)
|
initUpdater(win)
|
||||||
|
|
||||||
win.on('closed', () => {
|
win.on('closed', () => {
|
||||||
@@ -45,7 +68,7 @@ function createWindow () {
|
|||||||
if (process.platform === 'darwin') {
|
if (process.platform === 'darwin') {
|
||||||
app.dock.show()
|
app.dock.show()
|
||||||
}
|
}
|
||||||
if (process.platform === 'Linux') {
|
if (process.platform === 'linux') {
|
||||||
app.disableHardwareAcceleration()
|
app.disableHardwareAcceleration()
|
||||||
app.commandLine.appendSwitch('--no-sandbox') // linux 关闭沙盒模式
|
app.commandLine.appendSwitch('--no-sandbox') // linux 关闭沙盒模式
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,13 +14,13 @@
|
|||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
</span>
|
</span>
|
||||||
<span :class="[view === 'Recommendation' ? 'active ': ''] + 'zy-svg'" @click="changeView('Recommendation')">
|
<!-- <span :class="[view === 'Recommendation' ? 'active ': ''] + 'zy-svg'" @click="changeView('Recommendation')">
|
||||||
<svg role="img" xmlns="http://www.w3.org/2000/svg" width="48px" height="48px" viewBox="0 0 24 24" aria-labelledby="thumbUpIconTitle" stroke="#2329D6" stroke-width="1" stroke-linecap="square" stroke-linejoin="miter" fill="none" color="#2329D6">
|
<svg role="img" xmlns="http://www.w3.org/2000/svg" width="48px" height="48px" viewBox="0 0 24 24" aria-labelledby="thumbUpIconTitle" stroke="#2329D6" stroke-width="1" stroke-linecap="square" stroke-linejoin="miter" fill="none" color="#2329D6">
|
||||||
<title id="thumbUpIconTitle">影视推荐</title>
|
<title id="thumbUpIconTitle">影视推荐</title>
|
||||||
<path d="M8,8.73984815 C8,8.26242561 8.17078432,7.80075162 8.4814868,7.43826541 L13.2723931,1.84887469 C13.7000127,1.34998522 14.4122932,1.20614658 15,1.5 C15.5737957,1.78689785 15.849314,2.45205792 15.6464466,3.06066017 L14,8 L18.6035746,8 C18.7235578,8 18.8432976,8.01079693 18.9613454,8.03226018 C20.0480981,8.22985158 20.7689058,9.27101818 20.5713144,10.3577709 L19.2985871,17.3577709 C19.1256814,18.3087523 18.2974196,19 17.3308473,19 L10,19 C8.8954305,19 8,18.1045695 8,17 L8,8.73984815 Z"/>
|
<path d="M8,8.73984815 C8,8.26242561 8.17078432,7.80075162 8.4814868,7.43826541 L13.2723931,1.84887469 C13.7000127,1.34998522 14.4122932,1.20614658 15,1.5 C15.5737957,1.78689785 15.849314,2.45205792 15.6464466,3.06066017 L14,8 L18.6035746,8 C18.7235578,8 18.8432976,8.01079693 18.9613454,8.03226018 C20.0480981,8.22985158 20.7689058,9.27101818 20.5713144,10.3577709 L19.2985871,17.3577709 C19.1256814,18.3087523 18.2974196,19 17.3308473,19 L10,19 C8.8954305,19 8,18.1045695 8,17 L8,8.73984815 Z"/>
|
||||||
<path d="M4,18 L4,9"/>
|
<path d="M4,18 L4,9"/>
|
||||||
</svg>
|
</svg>
|
||||||
</span>
|
</span> -->
|
||||||
<span :class="[view === 'Play' ? 'active ': ''] + 'zy-svg'" @click="changeView('Play')">
|
<span :class="[view === 'Play' ? 'active ': ''] + 'zy-svg'" @click="changeView('Play')">
|
||||||
<svg role="img" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" aria-labelledby="playIconTitle">
|
<svg role="img" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" aria-labelledby="playIconTitle">
|
||||||
<title id="playIconTitle">播放</title>
|
<title id="playIconTitle">播放</title>
|
||||||
@@ -53,6 +53,12 @@
|
|||||||
import { mapMutations } from 'vuex'
|
import { mapMutations } from 'vuex'
|
||||||
export default {
|
export default {
|
||||||
name: 'Aside',
|
name: 'Aside',
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
lastViewOpenDetail: '',
|
||||||
|
savedDetail: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
view: {
|
view: {
|
||||||
get () {
|
get () {
|
||||||
@@ -74,9 +80,19 @@ export default {
|
|||||||
methods: {
|
methods: {
|
||||||
...mapMutations(['SET_VIEW', 'SET_DETAIL']),
|
...mapMutations(['SET_VIEW', 'SET_DETAIL']),
|
||||||
changeView (e) {
|
changeView (e) {
|
||||||
|
// 记录打开detail的view
|
||||||
|
if (this.detail.show === true) {
|
||||||
|
this.lastViewOpenDetail = this.view
|
||||||
|
this.savedDetail = this.detail
|
||||||
|
}
|
||||||
this.view = e
|
this.view = e
|
||||||
this.detail = {
|
// 如果回到上一次打开detail的试图页面,恢复detail页面
|
||||||
show: false
|
if (e === this.lastViewOpenDetail) {
|
||||||
|
this.detail = this.savedDetail
|
||||||
|
} else {
|
||||||
|
this.detail = {
|
||||||
|
show: false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
</svg>
|
</svg>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="detail-body zy-scroll" v-show="!loading">
|
<div class="detail-body zy-scroll listpage" v-show="!loading">
|
||||||
<div class="info">
|
<div class="info">
|
||||||
<div class="info-left">
|
<div class="info-left">
|
||||||
<img :src="info.pic" alt="">
|
<img :src="info.pic" alt="">
|
||||||
@@ -36,10 +36,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="operate">
|
<div class="operate">
|
||||||
<span @click="playEvent(0)">播放</span>
|
<span @click="playEvent(selectedEpisode)">播放</span>
|
||||||
<span @click="starEvent">收藏</span>
|
<span @click="starEvent(info)">收藏</span>
|
||||||
<span @click="downloadEvent">下载</span>
|
<span @click="downloadEvent">下载</span>
|
||||||
<span @click="shareEvent">分享</span>
|
<span @click="shareEvent(info,selectedEpisode)">分享</span>
|
||||||
<span @click="doubanLinkEvent">豆瓣</span>
|
<span @click="doubanLinkEvent">豆瓣</span>
|
||||||
<span @click="togglePlayOnlineEvent">
|
<span @click="togglePlayOnlineEvent">
|
||||||
<input type="checkbox" v-model="playOnline"> 播放在线高清视频
|
<input type="checkbox" v-model="playOnline"> 播放在线高清视频
|
||||||
@@ -52,10 +52,57 @@
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="desc" v-show="info.des">{{info.des}}</div>
|
class="desc" v-show="info.des">{{info.des}}
|
||||||
|
</div>
|
||||||
|
<div class="m3u8" v-if="videoFullList.length > 1">
|
||||||
|
<div class="box">
|
||||||
|
<span v-bind:class="{ selected: i.flag === videoFlag }" v-for="(i, j) in videoFullList" :key="j" @click="updateVideoList(i)">{{i.flag}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="m3u8">
|
<div class="m3u8">
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<span v-for="(i, j) in m3u8List" :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 class="m3u8">
|
||||||
|
<div class="show-picture" v-show="info.recommendations && info.recommendations.length > 0">
|
||||||
|
<span>喜欢这部电影的人也喜欢 · · · · · ·</span>
|
||||||
|
<Waterfall :list="info.recommendations" :gutter="20" :width="240"
|
||||||
|
:breakpoints="{
|
||||||
|
1200: { //当屏幕宽度小于等于1200
|
||||||
|
rowPerView: 4,
|
||||||
|
},
|
||||||
|
800: { //当屏幕宽度小于等于800
|
||||||
|
rowPerView: 3,
|
||||||
|
},
|
||||||
|
500: { //当屏幕宽度小于等于500
|
||||||
|
rowPerView: 2,
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
animationEffect="fadeIn"
|
||||||
|
backgroundColor="rgba(0, 0, 0, 0)">
|
||||||
|
<template slot="item" slot-scope="props">
|
||||||
|
<div class="card">
|
||||||
|
<div class="img">
|
||||||
|
<img style="width: 100%" :src="props.data.pic" alt="" @click="detailEvent(props.data)">
|
||||||
|
<div class="operate">
|
||||||
|
<div class="operate-wrap">
|
||||||
|
<span class="o-play" @click="playRecommendationEvent(props.data)">播放</span>
|
||||||
|
<span class="o-star" @click="starEvent(props.data)">收藏</span>
|
||||||
|
<span class="o-share" @click="shareEvent(props.data, 0)">分享</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="name">{{props.data.name}}</div>
|
||||||
|
<div class="info">
|
||||||
|
<span>{{props.data.area}}</span>
|
||||||
|
<span>{{props.data.year}}</span>
|
||||||
|
<span>{{props.data.note}}</span>
|
||||||
|
<span>{{props.data.type}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</Waterfall>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -67,6 +114,7 @@
|
|||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import { mapMutations } from 'vuex'
|
import { mapMutations } from 'vuex'
|
||||||
|
import Waterfall from 'vue-waterfall-plugin'
|
||||||
import zy from '../lib/site/tools'
|
import zy from '../lib/site/tools'
|
||||||
import onlineVideo from '../lib/site/onlineVideo'
|
import onlineVideo from '../lib/site/onlineVideo'
|
||||||
import { star, history } from '../lib/dexie'
|
import { star, history } from '../lib/dexie'
|
||||||
@@ -76,17 +124,26 @@ export default {
|
|||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
loading: true,
|
loading: true,
|
||||||
m3u8List: [],
|
videoFlag: '',
|
||||||
|
videoList: [],
|
||||||
|
videoFullList: [],
|
||||||
|
key: '',
|
||||||
|
site: {},
|
||||||
info: {},
|
info: {},
|
||||||
playOnline: false,
|
playOnline: false,
|
||||||
|
selectedEpisode: 0, // 选定集数
|
||||||
selectedOnlineSite: '哔嘀',
|
selectedOnlineSite: '哔嘀',
|
||||||
onlineSites: ['哔嘀', '素白白', '简影', '极品', '喜欢看', '1080影视']
|
onlineSites: ['哔嘀', '素白白', '简影', '极品', '喜欢看', '1080影视']
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
filters: {
|
filters: {
|
||||||
ftName (e) {
|
ftName (e, n) {
|
||||||
const name = e.split('$')[0]
|
const num = e.split('$')
|
||||||
return name
|
if (num.length > 1) {
|
||||||
|
return e.split('$')[0]
|
||||||
|
} else {
|
||||||
|
return `第${(n + 1)}集`
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -121,26 +178,66 @@ export default {
|
|||||||
set (val) {
|
set (val) {
|
||||||
this.SET_SHARE(val)
|
this.SET_SHARE(val)
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
DetailCache: {
|
||||||
|
get () {
|
||||||
|
return this.$store.getters.getDetailCache
|
||||||
|
},
|
||||||
|
set (val) {
|
||||||
|
this.SET_DetailCache(val)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
components: {
|
||||||
|
Waterfall
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapMutations(['SET_VIEW', 'SET_VIDEO', 'SET_DETAIL', 'SET_SHARE']),
|
...mapMutations(['SET_VIEW', 'SET_VIDEO', 'SET_DETAIL', 'SET_SHARE', 'SET_DetailCache']),
|
||||||
|
async playRecommendationEvent (e) {
|
||||||
|
const db = await history.find({ site: this.detail.key, ids: e.id })
|
||||||
|
if (db) {
|
||||||
|
this.video = { key: db.site, info: { id: db.ids, name: db.name, index: db.index, site: this.detail.site } }
|
||||||
|
} else {
|
||||||
|
this.video = { key: this.detail.key, info: { id: e.id, name: e.name, index: 0, site: this.detail.site } }
|
||||||
|
}
|
||||||
|
this.video.detail = e
|
||||||
|
this.view = 'Play'
|
||||||
|
this.detail.show = false
|
||||||
|
},
|
||||||
|
addClass (flag) {
|
||||||
|
if (flag === this.videoFlag) {
|
||||||
|
return 'selectedBox'
|
||||||
|
} else {
|
||||||
|
return 'box'
|
||||||
|
}
|
||||||
|
},
|
||||||
close () {
|
close () {
|
||||||
this.detail.show = false
|
this.detail.show = false
|
||||||
},
|
},
|
||||||
|
async updateVideoList (e) {
|
||||||
|
this.videoFlag = e.flag
|
||||||
|
this.videoList = e.list
|
||||||
|
const db = await history.find({ site: this.detail.key, ids: this.detail.info.id })
|
||||||
|
if (db) {
|
||||||
|
const doc = { ...db }
|
||||||
|
doc.videoFlag = e.flag
|
||||||
|
delete doc.id
|
||||||
|
history.update(db.id, doc)
|
||||||
|
}
|
||||||
|
},
|
||||||
async playEvent (n) {
|
async playEvent (n) {
|
||||||
if (!this.playOnline) {
|
if (!this.playOnline) {
|
||||||
const db = await history.find({ site: this.detail.key, ids: this.detail.info.id })
|
const db = await history.find({ site: this.detail.key, ids: this.detail.info.id })
|
||||||
if (db) {
|
if (db) {
|
||||||
this.video = { key: db.site, info: { id: db.ids, name: db.name, index: n, site: this.detail.site } }
|
this.video = { key: db.site, info: { id: db.ids, name: db.name, index: n, site: this.detail.site, videoFlag: this.videoFlag } }
|
||||||
} else {
|
} else {
|
||||||
this.video = { key: this.detail.key, info: { id: this.detail.info.id, name: this.detail.info.name, index: n, site: this.detail.site } }
|
this.video = { key: this.detail.key, info: { id: this.detail.info.id, name: this.detail.info.name, index: n, site: this.detail.site, videoFlag: this.videoFlag } }
|
||||||
}
|
}
|
||||||
this.video.detail = this.info
|
this.video.detail = this.info
|
||||||
this.view = 'Play'
|
this.view = 'Play'
|
||||||
this.detail.show = false
|
this.detail.show = false
|
||||||
} else {
|
} else {
|
||||||
const db = await history.find({ site: this.detail.key, ids: this.detail.info.id })
|
const db = await history.find({ site: this.detail.key, ids: this.info.id })
|
||||||
if (db) {
|
if (db) {
|
||||||
db.index = n
|
db.index = n
|
||||||
db.detail = this.info
|
db.detail = this.info
|
||||||
@@ -161,15 +258,15 @@ export default {
|
|||||||
onlineVideo.playVideoOnline(this.selectedOnlineSite, this.detail.info.name, n)
|
onlineVideo.playVideoOnline(this.selectedOnlineSite, this.detail.info.name, n)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async starEvent () {
|
async starEvent (info) {
|
||||||
const db = await star.find({ key: this.detail.key, ids: this.info.id })
|
const db = await star.find({ key: this.detail.key, ids: info.id })
|
||||||
const doc = {
|
const doc = {
|
||||||
key: this.detail.key,
|
key: this.detail.key,
|
||||||
ids: this.info.id,
|
ids: info.id,
|
||||||
site: this.detail.site,
|
site: this.detail.site,
|
||||||
name: this.info.name,
|
name: info.name,
|
||||||
detail: this.info,
|
detail: info,
|
||||||
rate: this.info.rate
|
rate: info.rate
|
||||||
}
|
}
|
||||||
if (db) {
|
if (db) {
|
||||||
star.update(db.id, doc)
|
star.update(db.id, doc)
|
||||||
@@ -180,6 +277,10 @@ export default {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
detailEvent (info) {
|
||||||
|
this.detail.info = info
|
||||||
|
this.getDetailInfo()
|
||||||
|
},
|
||||||
togglePlayOnlineEvent () {
|
togglePlayOnlineEvent () {
|
||||||
this.playOnline = !this.playOnline
|
this.playOnline = !this.playOnline
|
||||||
},
|
},
|
||||||
@@ -211,64 +312,70 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
downloadEvent () {
|
downloadEvent () {
|
||||||
zy.download(this.detail.key, this.info.id).then(res => {
|
zy.download(this.detail.key, this.info.id, this.videoFlag).then(res => {
|
||||||
if (res && res.dl && res.dl.dd) {
|
clipboard.writeText(res.downloadUrls)
|
||||||
const text = res.dl.dd._t
|
this.$message.success(res.info)
|
||||||
if (text) {
|
}).catch((err) => {
|
||||||
const list = text.split('#')
|
this.$message.error(err.info)
|
||||||
let downloadUrl = res.name + '\n'
|
|
||||||
for (const i of list) {
|
|
||||||
const url = encodeURI(i.split('$')[1])
|
|
||||||
downloadUrl += (url + '\n')
|
|
||||||
}
|
|
||||||
clipboard.writeText(downloadUrl)
|
|
||||||
this.$message.success('『MP4』格式的链接已复制, 快去下载吧!')
|
|
||||||
} else {
|
|
||||||
this.$message.warning('没有查询到下载链接.')
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const list = [...this.m3u8List]
|
|
||||||
let downloadUrl = this.detail.info.name + '\n'
|
|
||||||
for (const i of list) {
|
|
||||||
const url = encodeURI(i.split('$')[1])
|
|
||||||
downloadUrl += (url + '\n')
|
|
||||||
}
|
|
||||||
clipboard.writeText(downloadUrl)
|
|
||||||
this.$message.success('『M3U8』格式的链接已复制, 快去下载吧!')
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
shareEvent () {
|
shareEvent (info, selectedEpisode) {
|
||||||
this.share = {
|
this.share = {
|
||||||
show: true,
|
show: true,
|
||||||
key: this.detail.key,
|
key: this.detail.key,
|
||||||
info: this.detail.info
|
info: info,
|
||||||
|
index: selectedEpisode
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
doubanLinkEvent () {
|
doubanLinkEvent () {
|
||||||
const name = this.detail.info.name.trim()
|
const name = this.info.name.trim()
|
||||||
zy.doubanLink(name).then(link => {
|
const year = this.info.year
|
||||||
|
zy.doubanLink(name, year).then(link => {
|
||||||
const open = require('open')
|
const open = require('open')
|
||||||
open(link)
|
open(link)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
getDoubanRate () {
|
async getDoubanRate () {
|
||||||
const name = this.detail.info.name.trim()
|
const name = this.info.name.trim()
|
||||||
zy.doubanRate(name).then(res => {
|
const year = this.info.year
|
||||||
this.info.rate = res
|
this.info.rate = await zy.doubanRate(name, year)
|
||||||
})
|
const recommendations = await zy.doubanRecommendations(name, year)
|
||||||
|
if (recommendations) {
|
||||||
|
this.info.recommendations = []
|
||||||
|
recommendations.forEach(element => {
|
||||||
|
zy.searchFirstDetail(this.detail.key, element).then(detailRes => {
|
||||||
|
if (detailRes) {
|
||||||
|
this.info.recommendations.push(detailRes)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
},
|
},
|
||||||
getDetailInfo () {
|
async getDetailInfo () {
|
||||||
const id = this.detail.info.ids || this.detail.info.id
|
const id = this.detail.info.ids || this.detail.info.id
|
||||||
zy.detail(this.detail.key, id).then(res => {
|
const cacheKey = this.detail.key + '@' + id
|
||||||
if (res) {
|
const db = await history.find({ site: this.detail.key, ids: id })
|
||||||
this.info = res
|
if (db) {
|
||||||
this.$set(this.info, 'rate', '')
|
this.videoFlag = db.videoFlag
|
||||||
this.m3u8List = res.m3u8List
|
this.selectedEpisode = db.index
|
||||||
this.getDoubanRate()
|
}
|
||||||
this.loading = false
|
if (!this.DetailCache[cacheKey]) {
|
||||||
|
this.DetailCache[cacheKey] = await zy.detail(this.detail.key, id)
|
||||||
|
}
|
||||||
|
const res = this.DetailCache[cacheKey]
|
||||||
|
if (res) {
|
||||||
|
this.info = res
|
||||||
|
this.$set(this.info, 'rate', this.DetailCache[cacheKey].rate || '')
|
||||||
|
this.$set(this.info, 'recommendations', this.DetailCache[cacheKey].recommendations || [])
|
||||||
|
this.videoFlag = this.videoFlag || res.fullList[0].flag
|
||||||
|
this.videoList = res.fullList[0].list
|
||||||
|
this.videoFullList = res.fullList
|
||||||
|
this.loading = false
|
||||||
|
if (!this.info.rate) {
|
||||||
|
await this.getDoubanRate()
|
||||||
|
this.DetailCache[cacheKey] = this.info
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created () {
|
created () {
|
||||||
@@ -390,6 +497,15 @@ export default {
|
|||||||
margin: 6px 10px 0px 0px;
|
margin: 6px 10px 0px 0px;
|
||||||
padding: 8px 22px;
|
padding: 8px 22px;
|
||||||
}
|
}
|
||||||
|
.selected {
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 12px;
|
||||||
|
border: 1px solid;
|
||||||
|
border-radius: 2px;
|
||||||
|
cursor: pointer;
|
||||||
|
margin: 6px 10px 0px 0px;
|
||||||
|
padding: 8px 22px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,18 +2,18 @@
|
|||||||
<div class="listpage" id="sites">
|
<div class="listpage" id="sites">
|
||||||
<div class="listpage-header" v-show="!enableBatchEdit">
|
<div class="listpage-header" v-show="!enableBatchEdit">
|
||||||
<el-switch v-model="enableBatchEdit" active-text="批处理分组">></el-switch>
|
<el-switch v-model="enableBatchEdit" active-text="批处理分组">></el-switch>
|
||||||
<el-checkbox v-model="setting.excludeR18Films" @change="excludeR18FilmsChangeEvent">屏蔽福利片</el-checkbox>
|
<el-button @click="openFilterKeywordsDiag" icon="el-icon-key">关键词过滤</el-button>
|
||||||
<el-button @click="addSite" icon="el-icon-document-add">新增</el-button>
|
<el-button @click="addSite" icon="el-icon-document-add">新增</el-button>
|
||||||
<el-button @click="exportSites" icon="el-icon-upload2" >导出</el-button>
|
<el-button @click="exportSites" icon="el-icon-upload2" title="导出全部,自动添加扩展名">导出</el-button>
|
||||||
<el-button @click="importSites" icon="el-icon-download">导入</el-button>
|
<el-button @click="importSites" icon="el-icon-download" title="支持同时导入多个文件">导入</el-button>
|
||||||
<el-button @click="checkAllSite" icon="el-icon-refresh" :loading="checkAllSitesLoading">检测{{ this.checkAllSitesLoading ? this.checkProgress + '/' + this.sites.length : '' }}</el-button>
|
<el-button @click="checkAllSite" icon="el-icon-refresh" :loading="checkAllSitesLoading" title="可在后台运行">检测{{ this.checkAllSitesLoading ? this.checkProgress + '/' + this.sites.length : '' }}</el-button>
|
||||||
<el-button @click="resetSitesEvent" icon="el-icon-refresh-left">重置</el-button>
|
<el-button @click="resetSitesEvent" icon="el-icon-refresh-left">重置</el-button>
|
||||||
</div>
|
</div>
|
||||||
<div class="listpage-header" v-show="enableBatchEdit">
|
<div class="listpage-header" v-show="enableBatchEdit">
|
||||||
<el-switch v-model="enableBatchEdit" active-text="批处理分组"></el-switch>
|
<el-switch v-model="enableBatchEdit" active-text="批处理分组"></el-switch>
|
||||||
<el-input placeholder="新组名" v-model="batchGroupName"></el-input>
|
<el-input placeholder="新组名" v-model="batchGroupName"></el-input>
|
||||||
<el-switch v-model="batchIsActive" active-text="启用"></el-switch>
|
<el-switch v-model="batchIsActive" active-text="启用"></el-switch>
|
||||||
<el-button type="primary" icon="el-icon-edit" @click.stop="saveBatchEdit">保存</el-button>
|
<el-button type="primary" icon="el-icon-edit" @click.stop="saveBatchEdit" title="输入框组名为空时仅保存开关状态">保存分组与开关状态</el-button>
|
||||||
<el-button @click="removeSelectedSites" icon="el-icon-delete-solid">删除</el-button>
|
<el-button @click="removeSelectedSites" icon="el-icon-delete-solid">删除</el-button>
|
||||||
</div>
|
</div>
|
||||||
<div class="listpage-body" id="sites-body">
|
<div class="listpage-body" id="sites-body">
|
||||||
@@ -41,7 +41,18 @@
|
|||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<el-switch
|
<el-switch
|
||||||
v-model="scope.row.isActive"
|
v-model="scope.row.isActive"
|
||||||
@click.native.stop='isActiveChangeEvent(scope.row)'>
|
@click.native.stop='propChangeEvent(scope.row)'>
|
||||||
|
</el-switch>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
prop="reverseOrder"
|
||||||
|
width="120"
|
||||||
|
label="倒序排列">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-switch
|
||||||
|
v-model="scope.row.reverseOrder"
|
||||||
|
@click.native.stop='propChangeEvent(scope.row)'>>
|
||||||
</el-switch>
|
</el-switch>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
@@ -83,7 +94,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<!-- 编辑页面 -->
|
<!-- 编辑页面 -->
|
||||||
<div>
|
<div>
|
||||||
<el-dialog :visible.sync="dialogVisible" v-if='dialogVisible' :title="dialogType==='edit'?'编辑源':'新增源'" :append-to-body="true" @close="closeDialog">
|
<el-dialog :visible.sync="editSiteDialogVisible" v-if='editSiteDialogVisible' :title="dialogType==='edit'?'编辑源':'新增源'" :append-to-body="true" @close="closeDialog">
|
||||||
<el-form :model="siteInfo" ref='siteInfo' label-width="75px" label-position="left" :rules="rules">
|
<el-form :model="siteInfo" ref='siteInfo' label-width="75px" label-position="left" :rules="rules">
|
||||||
<el-form-item label="源站名" prop='name'>
|
<el-form-item label="源站名" prop='name'>
|
||||||
<el-input v-model="siteInfo.name" placeholder="请输入源站名" />
|
<el-input v-model="siteInfo.name" placeholder="请输入源站名" />
|
||||||
@@ -94,6 +105,9 @@
|
|||||||
<el-form-item label="下载接口" prop='download'>
|
<el-form-item label="下载接口" prop='download'>
|
||||||
<el-input v-model="siteInfo.download" :autosize="{ minRows: 2, maxRows: 4}" type="textarea" placeholder="请输入Download接口地址,可以空着"/>
|
<el-input v-model="siteInfo.download" :autosize="{ minRows: 2, maxRows: 4}" type="textarea" placeholder="请输入Download接口地址,可以空着"/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item label="解析接口" prop='jiexiUrl'>
|
||||||
|
<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-form-item label="分组" prop='group'>
|
||||||
<el-select v-model="siteInfo.group" allow-create filterable default-first-option placeholder="请输入分组">
|
<el-select v-model="siteInfo.group" allow-create filterable default-first-option placeholder="请输入分组">
|
||||||
<el-option v-for="item in siteGroup" :key="item" :label="item" :value="item"></el-option>
|
<el-option v-for="item in siteGroup" :key="item" :label="item" :value="item"></el-option>
|
||||||
@@ -109,16 +123,36 @@
|
|||||||
</span>
|
</span>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- 设置过滤关键词页面 -->
|
||||||
|
<div>
|
||||||
|
<el-dialog :visible.sync="filterKeywordsDialogVisible" v-if='filterKeywordsDialogVisible' :title="'分类过滤'" :append-to-body="true" @close="closeDialog">
|
||||||
|
<el-form>
|
||||||
|
<el-switch v-model="excludeRootClasses" active-text="开启主分类过滤">></el-switch>
|
||||||
|
<el-form-item>
|
||||||
|
<el-input v-model="rootClassFilterKeywords" :autosize="{ minRows: 3, maxRows: 6}" type="textarea" placeholder="请输入过滤关键词" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<el-form>
|
||||||
|
<el-switch v-model="excludeR18Films" active-text="开启福利分类过滤">></el-switch>
|
||||||
|
<el-form-item>
|
||||||
|
<el-input v-model="r18ClassFilterKeywords" :autosize="{ minRows: 3, maxRows: 6}" type="textarea" placeholder="请输入过滤关键词" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<span slot="footer" class="dialog-footer">
|
||||||
|
<el-button @click="closeDialog">取消</el-button>
|
||||||
|
<el-button type="primary" @click="saveFilterKeywords">保存</el-button>
|
||||||
|
</span>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import { mapMutations } from 'vuex'
|
import { mapMutations } from 'vuex'
|
||||||
import { sites, setting } from '../lib/dexie'
|
import { sites, setting } from '../lib/dexie'
|
||||||
import zy from '../lib/site/tools'
|
import zy from '../lib/site/tools'
|
||||||
import { remote } from 'electron'
|
|
||||||
import { sites as defaultSites } from '../lib/dexie/initData'
|
|
||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
import Sortable from 'sortablejs'
|
import Sortable from 'sortablejs'
|
||||||
|
const remote = require('@electron/remote')
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'editSites',
|
name: 'editSites',
|
||||||
@@ -127,15 +161,21 @@ export default {
|
|||||||
show: false,
|
show: false,
|
||||||
sites: [],
|
sites: [],
|
||||||
dialogType: 'new',
|
dialogType: 'new',
|
||||||
dialogVisible: false,
|
editSiteDialogVisible: false,
|
||||||
|
filterKeywordsDialogVisible: false,
|
||||||
siteInfo: {
|
siteInfo: {
|
||||||
key: '',
|
key: '',
|
||||||
name: '',
|
name: '',
|
||||||
api: '',
|
api: '',
|
||||||
download: '',
|
download: '',
|
||||||
|
jiexiUrl: '',
|
||||||
group: '',
|
group: '',
|
||||||
isActive: true
|
isActive: true
|
||||||
},
|
},
|
||||||
|
excludeRootClasses: true,
|
||||||
|
excludeR18Films: true,
|
||||||
|
rootClassFilterKeywords: [],
|
||||||
|
r18ClassFilterKeywords: [],
|
||||||
siteGroup: [],
|
siteGroup: [],
|
||||||
rules: {
|
rules: {
|
||||||
name: [
|
name: [
|
||||||
@@ -169,9 +209,9 @@ export default {
|
|||||||
},
|
},
|
||||||
getFilters () {
|
getFilters () {
|
||||||
const groups = [...new Set(this.sites.map(site => site.group))]
|
const groups = [...new Set(this.sites.map(site => site.group))]
|
||||||
var filters = []
|
const filters = []
|
||||||
groups.forEach(g => {
|
groups.forEach(g => {
|
||||||
var doc = {
|
const doc = {
|
||||||
text: g,
|
text: g,
|
||||||
value: g
|
value: g
|
||||||
}
|
}
|
||||||
@@ -186,16 +226,21 @@ export default {
|
|||||||
this.$message.info('正在检测, 请勿操作.')
|
this.$message.info('正在检测, 请勿操作.')
|
||||||
this.enableBatchEdit = false
|
this.enableBatchEdit = false
|
||||||
}
|
}
|
||||||
|
if (this.enableBatchEdit) {
|
||||||
|
if (this.setting.shiftTooltipLimitTimes === undefined) this.setting.shiftTooltipLimitTimes = 5
|
||||||
|
if (this.setting.shiftTooltipLimitTimes) {
|
||||||
|
this.$message.info('多选时支持shift快捷键')
|
||||||
|
this.setting.shiftTooltipLimitTimes--
|
||||||
|
setting.find().then(res => {
|
||||||
|
res.shiftTooltipLimitTimes = this.setting.shiftTooltipLimitTimes
|
||||||
|
setting.update(res)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapMutations(['SET_SETTING']),
|
...mapMutations(['SET_SETTING']),
|
||||||
excludeR18FilmsChangeEvent () {
|
|
||||||
setting.find().then(res => {
|
|
||||||
res.excludeR18Films = this.setting.excludeR18Films
|
|
||||||
setting.update(res)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
selectionCellClick (selection, row) {
|
selectionCellClick (selection, row) {
|
||||||
if (this.shiftDown && this.selectionBegin !== '' && selection.includes(row)) {
|
if (this.shiftDown && this.selectionBegin !== '' && selection.includes(row)) {
|
||||||
this.selectionEnd = row.id
|
this.selectionEnd = row.id
|
||||||
@@ -235,6 +280,11 @@ export default {
|
|||||||
},
|
},
|
||||||
getSites () {
|
getSites () {
|
||||||
sites.all().then(res => {
|
sites.all().then(res => {
|
||||||
|
res.forEach(element => {
|
||||||
|
if (element.reverseOrder === null || element.reverseOrder === undefined) {
|
||||||
|
element.reverseOrder = false
|
||||||
|
}
|
||||||
|
})
|
||||||
this.sites = res
|
this.sites = res
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
@@ -247,6 +297,29 @@ export default {
|
|||||||
}
|
}
|
||||||
this.siteGroup = arr
|
this.siteGroup = arr
|
||||||
},
|
},
|
||||||
|
openFilterKeywordsDiag () {
|
||||||
|
this.excludeRootClasses = this.setting.excludeRootClasses
|
||||||
|
this.excludeR18Films = this.setting.excludeR18Films
|
||||||
|
this.rootClassFilterKeywords = this.setting.rootClassFilter?.join()
|
||||||
|
this.r18ClassFilterKeywords = this.setting.r18ClassFilter?.join()
|
||||||
|
this.filterKeywordsDialogVisible = true
|
||||||
|
},
|
||||||
|
saveFilterKeywords () {
|
||||||
|
// 移除空格,然后按逗号分开
|
||||||
|
this.setting.rootClassFilter = this.rootClassFilterKeywords?.replace(/\s/g, '').split(',')
|
||||||
|
this.setting.r18ClassFilter = this.r18ClassFilterKeywords?.replace(/\s/g, '').split(',')
|
||||||
|
this.setting.classFilter = []
|
||||||
|
this.setting.excludeRootClasses = this.excludeRootClasses
|
||||||
|
if (this.excludeRootClasses) {
|
||||||
|
this.setting.classFilter = this.setting.classFilter.concat(this.setting.rootClassFilter)
|
||||||
|
}
|
||||||
|
this.setting.excludeR18Films = this.excludeR18Films
|
||||||
|
if (this.excludeR18Films) {
|
||||||
|
this.setting.classFilter = this.setting.classFilter.concat(this.setting.r18ClassFilter)
|
||||||
|
}
|
||||||
|
setting.update(this.setting)
|
||||||
|
this.filterKeywordsDialogVisible = false
|
||||||
|
},
|
||||||
addSite () {
|
addSite () {
|
||||||
if (this.checkAllSitesLoading) {
|
if (this.checkAllSitesLoading) {
|
||||||
this.$message.info('正在检测, 请勿操作.')
|
this.$message.info('正在检测, 请勿操作.')
|
||||||
@@ -254,12 +327,13 @@ export default {
|
|||||||
}
|
}
|
||||||
this.getSitesGroup()
|
this.getSitesGroup()
|
||||||
this.dialogType = 'new'
|
this.dialogType = 'new'
|
||||||
this.dialogVisible = true
|
this.editSiteDialogVisible = true
|
||||||
this.siteInfo = {
|
this.siteInfo = {
|
||||||
key: '',
|
key: '',
|
||||||
name: '',
|
name: '',
|
||||||
api: '',
|
api: '',
|
||||||
download: '',
|
download: '',
|
||||||
|
jiexiUrl: '',
|
||||||
group: '',
|
group: '',
|
||||||
isActive: true
|
isActive: true
|
||||||
}
|
}
|
||||||
@@ -271,12 +345,13 @@ export default {
|
|||||||
}
|
}
|
||||||
this.getSitesGroup()
|
this.getSitesGroup()
|
||||||
this.dialogType = 'edit'
|
this.dialogType = 'edit'
|
||||||
this.dialogVisible = true
|
this.editSiteDialogVisible = true
|
||||||
this.siteInfo = siteInfo
|
this.siteInfo = siteInfo
|
||||||
this.editOldkey = siteInfo.key
|
this.editOldkey = siteInfo.key
|
||||||
},
|
},
|
||||||
closeDialog () {
|
closeDialog () {
|
||||||
this.dialogVisible = false
|
this.editSiteDialogVisible = false
|
||||||
|
this.filterKeywordsDialogVisible = false
|
||||||
this.getSites()
|
this.getSites()
|
||||||
},
|
},
|
||||||
removeEvent (e) {
|
removeEvent (e) {
|
||||||
@@ -311,13 +386,14 @@ export default {
|
|||||||
if (!this.checkSiteKey()) {
|
if (!this.checkSiteKey()) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
var randomstring = require('randomstring')
|
const randomstring = require('randomstring')
|
||||||
var doc = {
|
const doc = {
|
||||||
key: this.dialogType === 'edit' ? this.siteInfo.key : this.siteInfo.key ? this.siteInfo.key : randomstring.generate(6),
|
key: this.dialogType === 'edit' ? this.siteInfo.key : this.siteInfo.key ? this.siteInfo.key : randomstring.generate(6),
|
||||||
id: this.dialogType === 'edit' ? this.siteInfo.id : this.sites.length ? this.sites[this.sites.length - 1].id + 1 : 1,
|
id: this.dialogType === 'edit' ? this.siteInfo.id : this.sites.length ? this.sites[this.sites.length - 1].id + 1 : 1,
|
||||||
name: this.siteInfo.name,
|
name: this.siteInfo.name,
|
||||||
api: this.siteInfo.api,
|
api: this.siteInfo.api,
|
||||||
download: this.siteInfo.download,
|
download: this.siteInfo.download,
|
||||||
|
jiexiUrl: this.siteInfo.jiexiUrl,
|
||||||
group: this.siteInfo.group,
|
group: this.siteInfo.group,
|
||||||
isActive: this.siteInfo.isActive
|
isActive: this.siteInfo.isActive
|
||||||
}
|
}
|
||||||
@@ -328,10 +404,11 @@ export default {
|
|||||||
name: '',
|
name: '',
|
||||||
api: '',
|
api: '',
|
||||||
download: '',
|
download: '',
|
||||||
|
jiexiUrl: '',
|
||||||
group: ''
|
group: ''
|
||||||
}
|
}
|
||||||
this.dialogType === 'edit' ? this.$message.success('修改成功!') : this.$message.success('新增源成功!')
|
this.dialogType === 'edit' ? this.$message.success('修改成功!') : this.$message.success('新增源成功!')
|
||||||
this.dialogVisible = false
|
this.editSiteDialogVisible = false
|
||||||
this.getSites()
|
this.getSites()
|
||||||
})
|
})
|
||||||
this.editOldkey = ''
|
this.editOldkey = ''
|
||||||
@@ -342,13 +419,12 @@ export default {
|
|||||||
const str = JSON.stringify(arr, null, 2)
|
const str = JSON.stringify(arr, null, 2)
|
||||||
const options = {
|
const options = {
|
||||||
filters: [
|
filters: [
|
||||||
{ name: 'JSON file', extensions: ['json'] },
|
{ name: 'JSON file', extensions: ['json'] }
|
||||||
{ name: 'Normal text file', extensions: ['txt'] },
|
|
||||||
{ name: 'All types', extensions: ['*'] }
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
remote.dialog.showSaveDialog(options).then(result => {
|
remote.dialog.showSaveDialog(options).then(result => {
|
||||||
if (!result.canceled) {
|
if (!result.canceled) {
|
||||||
|
if (!result.filePath.endsWith('.json')) result.filePath += '.json'
|
||||||
fs.writeFileSync(result.filePath, str)
|
fs.writeFileSync(result.filePath, str)
|
||||||
this.$message.success('已保存成功')
|
this.$message.success('已保存成功')
|
||||||
}
|
}
|
||||||
@@ -363,45 +439,75 @@ export default {
|
|||||||
}
|
}
|
||||||
const options = {
|
const options = {
|
||||||
filters: [
|
filters: [
|
||||||
{ name: 'JSON file', extensions: ['json'] },
|
{ name: '支持的文件格式', extensions: ['json', 'txt'] }
|
||||||
{ name: 'Normal text file', extensions: ['txt'] },
|
|
||||||
{ name: 'All types', extensions: ['*'] }
|
|
||||||
],
|
],
|
||||||
properties: ['openFile', 'multiSelections']
|
properties: ['openFile', 'multiSelections']
|
||||||
}
|
}
|
||||||
remote.dialog.showOpenDialog(options).then(result => {
|
remote.dialog.showOpenDialog(options).then(result => {
|
||||||
if (!result.canceled) {
|
if (!result.canceled) {
|
||||||
result.filePaths.forEach(file => {
|
result.filePaths.forEach(file => {
|
||||||
var str = fs.readFileSync(file)
|
if (file.endsWith('json')) {
|
||||||
const json = JSON.parse(str)
|
const str = fs.readFileSync(file)
|
||||||
json.forEach(ele => {
|
const json = JSON.parse(str)
|
||||||
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) {
|
json.forEach(ele => {
|
||||||
// 不含该key 同时也不含名字和url一样的
|
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) {
|
||||||
if (ele.isActive === undefined) {
|
// 不含该key 同时也不含名字和url一样的
|
||||||
ele.isActive = true
|
if (ele.isActive === undefined) {
|
||||||
|
ele.isActive = true
|
||||||
|
}
|
||||||
|
if (ele.group === undefined) {
|
||||||
|
ele.group = '导入'
|
||||||
|
}
|
||||||
|
this.sites.push(ele)
|
||||||
}
|
}
|
||||||
if (ele.group === undefined) {
|
})
|
||||||
ele.group = '导入'
|
this.resetId(this.sites)
|
||||||
}
|
sites.clear().then(sites.bulkAdd(this.sites))
|
||||||
this.sites.push(ele)
|
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 () {
|
resetSitesEvent () {
|
||||||
this.stopFlag = true
|
let url = this.setting.sitesDataURL
|
||||||
if (this.checkAllSitesLoading) {
|
if (!url) {
|
||||||
this.$message.info('部分检测还未完全终止, 请稍等...')
|
url = 'https://raw.iqiq.io/Hunlongyu/ZY-Player-Resources/main/Sites/20220713.json'
|
||||||
return
|
|
||||||
}
|
}
|
||||||
sites.clear().then(sites.bulkAdd(defaultSites).then(this.getSites()))
|
zy.getDefaultSites(url).then(res => {
|
||||||
this.$message.success('重置源成功')
|
if (res.length > 0) {
|
||||||
|
sites.clear().then(sites.bulkAdd(res))
|
||||||
|
this.$message.success('重置源成功')
|
||||||
|
this.getSites()
|
||||||
|
}
|
||||||
|
}).catch(error => {
|
||||||
|
this.$message.error('导入云端源站失败. ' + error)
|
||||||
|
})
|
||||||
},
|
},
|
||||||
moveToTopEvent (i) {
|
moveToTopEvent (i) {
|
||||||
if (this.checkAllSitesLoading) {
|
if (this.checkAllSitesLoading) {
|
||||||
@@ -416,12 +522,13 @@ export default {
|
|||||||
this.sites = this.$refs.editSitesTable.tableData
|
this.sites = this.$refs.editSitesTable.tableData
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
isActiveChangeEvent (row) {
|
propChangeEvent (row) {
|
||||||
sites.remove(row.id)
|
sites.remove(row.id)
|
||||||
sites.add(row)
|
sites.add(row)
|
||||||
|
this.getSites()
|
||||||
},
|
},
|
||||||
resetId (inArray) {
|
resetId (inArray) {
|
||||||
var id = 1
|
let id = 1
|
||||||
inArray.forEach(ele => {
|
inArray.forEach(ele => {
|
||||||
ele.id = id
|
ele.id = id
|
||||||
id += 1
|
id += 1
|
||||||
@@ -431,7 +538,7 @@ export default {
|
|||||||
// 因为el-table的数据是单向绑定,我们先同步el-table里的数据和其绑定的数据
|
// 因为el-table的数据是单向绑定,我们先同步el-table里的数据和其绑定的数据
|
||||||
this.syncTableData()
|
this.syncTableData()
|
||||||
sites.clear().then(res => {
|
sites.clear().then(res => {
|
||||||
var id = 1
|
let id = 1
|
||||||
this.sites.forEach(ele => {
|
this.sites.forEach(ele => {
|
||||||
ele.id = id
|
ele.id = id
|
||||||
id += 1
|
id += 1
|
||||||
@@ -452,7 +559,7 @@ export default {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
const tbody = document.getElementById('sites-table').querySelector('.el-table__body-wrapper tbody')
|
const tbody = document.getElementById('sites-table').querySelector('.el-table__body-wrapper tbody')
|
||||||
var _this = this
|
const _this = this
|
||||||
Sortable.create(tbody, {
|
Sortable.create(tbody, {
|
||||||
onEnd ({ newIndex, oldIndex }) {
|
onEnd ({ newIndex, oldIndex }) {
|
||||||
const currRow = _this.sites.splice(oldIndex, 1)[0]
|
const currRow = _this.sites.splice(oldIndex, 1)[0]
|
||||||
@@ -462,6 +569,7 @@ export default {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
async checkAllSite () {
|
async checkAllSite () {
|
||||||
|
if (this.checkAllSitesLoading) return
|
||||||
this.checkAllSitesLoading = true
|
this.checkAllSitesLoading = true
|
||||||
this.stopFlag = false
|
this.stopFlag = false
|
||||||
this.checkProgress = 0
|
this.checkProgress = 0
|
||||||
@@ -471,6 +579,7 @@ export default {
|
|||||||
await Promise.all(other.map(site => this.checkSingleSite(site))).then(res => {
|
await Promise.all(other.map(site => this.checkSingleSite(site))).then(res => {
|
||||||
this.checkAllSitesLoading = false
|
this.checkAllSitesLoading = false
|
||||||
this.getSites()
|
this.getSites()
|
||||||
|
if (!this.stopFlag) this.$message.success('视频点播源站批量检测已完成!')
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
async checkSingleSite (row) {
|
async checkSingleSite (row) {
|
||||||
|
|||||||
@@ -9,9 +9,7 @@
|
|||||||
:value="item.name">
|
:value="item.name">
|
||||||
</el-option>
|
</el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
<el-switch v-model="searchViewMode" active-text="海报" active-value="picture" inactive-text="列表" inactive-value="table" @change="updateSearchViewMode"
|
<el-select v-model="selectedClassName" size="small" placeholder="类型" :popper-append-to-body="false" popper-class="popper" @change="classClick" v-if="classList && classList.length" v-show="!showFind">
|
||||||
v-if="show.find"></el-switch>
|
|
||||||
<el-select v-model="selectedClassName" size="small" placeholder="类型" :popper-append-to-body="false" popper-class="popper" @change="classClick" v-show="show.class">
|
|
||||||
<el-option
|
<el-option
|
||||||
v-for="item in classList"
|
v-for="item in classList"
|
||||||
:key="item.tid"
|
:key="item.tid"
|
||||||
@@ -19,6 +17,14 @@
|
|||||||
:value="item.name">
|
:value="item.name">
|
||||||
</el-option>
|
</el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
|
<el-select v-model="selectedSearchClassNames" size="small" multiple placeholder="类型" :popper-append-to-body="false" popper-class="popper" v-if="searchClassList && searchClassList.length" v-show="showFind && showToolbar" @remove-tag="refreshFilteredList" @change="refreshFilteredList">
|
||||||
|
<el-option
|
||||||
|
v-for="(item, index) in searchClassList"
|
||||||
|
:key='index'
|
||||||
|
:label="item"
|
||||||
|
:value="item">
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
<el-autocomplete
|
<el-autocomplete
|
||||||
clearable
|
clearable
|
||||||
size="small"
|
size="small"
|
||||||
@@ -44,12 +50,42 @@
|
|||||||
</el-option>
|
</el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
<!--方便触屏-->
|
<!--方便触屏-->
|
||||||
<el-button icon="el-icon-search" @click.stop="searchEvent" slot="append" />
|
<el-button icon="el-icon-search" @click.stop="searchEvent" slot="append" v-if="!searchRunning"/>
|
||||||
|
<el-button icon="el-icon-loading" @click.stop="stopSearchEvent" slot="append" v-if="searchRunning" title='点击可停止搜索'/>
|
||||||
</el-autocomplete>
|
</el-autocomplete>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="toolbar" v-show="showToolbar">
|
||||||
|
<el-select v-model="selectedAreas" size="small" multiple placeholder="地区" popper-class="popper" :popper-append-to-body="false" @remove-tag="refreshFilteredList" @change="refreshFilteredList">
|
||||||
|
<el-option
|
||||||
|
v-for="item in areas"
|
||||||
|
:key="item"
|
||||||
|
:label="item"
|
||||||
|
:value="item">
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
<el-select v-model="sortKeyword" size="small" placeholder="排序" popper-class="popper" :popper-append-to-body="false" @change="refreshFilteredList">
|
||||||
|
<el-option
|
||||||
|
v-for="item in sortKeywords"
|
||||||
|
:key="item"
|
||||||
|
:label="item"
|
||||||
|
:value="item">
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
<span>
|
||||||
|
上映区间:
|
||||||
|
<el-input-number size="small" v-model="selectedYears.start" :min=0 :max="new Date().getFullYear()" controls-position="right" step-strictly @change="refreshFilteredList"></el-input-number>
|
||||||
|
至
|
||||||
|
<el-input-number size="small" v-model="selectedYears.end" :min=0 :max="new Date().getFullYear()" controls-position="right" step-strictly @change="refreshFilteredList"></el-input-number>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<el-divider class="listpage-header-divider" content-position="right">
|
||||||
|
<el-button type="text" size="mini" @click="toggleViewMode">视图切换</el-button>
|
||||||
|
<el-button type="text" size="mini" @click='() => { showToolbar = !showToolbar; if (!showToolbar) this.refreshFilteredList() }' title="收起工具栏会重置筛选排序">{{ showToolbar ? '隐藏工具栏' : '显示工具栏' }}</el-button>
|
||||||
|
<el-button type="text" size="mini" @click="backTop">回到顶部</el-button>
|
||||||
|
</el-divider>
|
||||||
<div class="listpage-body" id="film-body" infinite-wrapper>
|
<div class="listpage-body" id="film-body" infinite-wrapper>
|
||||||
<div class="show-picture" v-if="setting.view === 'picture' && !show.find">
|
<div class="show-picture" v-if="setting.view === 'picture' && !showFind">
|
||||||
<Waterfall ref="filmWaterfall" :list="list" :gutter="20" :width="240"
|
<Waterfall ref="filmWaterfall" :list="filteredList" :gutter="20" :width="240"
|
||||||
:breakpoints="{
|
:breakpoints="{
|
||||||
1200: { //当屏幕宽度小于等于1200
|
1200: { //当屏幕宽度小于等于1200
|
||||||
rowPerView: 4,
|
rowPerView: 4,
|
||||||
@@ -64,7 +100,7 @@
|
|||||||
animationEffect="fadeIn"
|
animationEffect="fadeIn"
|
||||||
backgroundColor="rgba(0, 0, 0, 0)">
|
backgroundColor="rgba(0, 0, 0, 0)">
|
||||||
<template slot="item" slot-scope="props">
|
<template slot="item" slot-scope="props">
|
||||||
<div class="card" v-show="!setting.excludeR18Films || !containsR18Keywords(props.data.type)">
|
<div class="card">
|
||||||
<div class="img">
|
<div class="img">
|
||||||
<img style="width: 100%" :src="props.data.pic" alt="" @load="$refs.filmWaterfall.refresh()" @click="detailEvent(site, props.data)">
|
<img style="width: 100%" :src="props.data.pic" alt="" @load="$refs.filmWaterfall.refresh()" @click="detailEvent(site, props.data)">
|
||||||
<div class="operate">
|
<div class="operate">
|
||||||
@@ -87,10 +123,11 @@
|
|||||||
</Waterfall>
|
</Waterfall>
|
||||||
<infinite-loading force-use-infinite-wrapper :identifier="infiniteId" @infinite="infiniteHandler"></infinite-loading>
|
<infinite-loading force-use-infinite-wrapper :identifier="infiniteId" @infinite="infiniteHandler"></infinite-loading>
|
||||||
</div>
|
</div>
|
||||||
<div class="show-table" v-if="setting.view === 'table' && !show.find">
|
<div class="show-table" v-if="setting.view === 'table' && !showFind">
|
||||||
<el-table
|
<el-table
|
||||||
size="mini"
|
size="mini"
|
||||||
:data="list.filter(res => !setting.excludeR18Films || !containsR18Keywords(res.type))"
|
:data="filteredList"
|
||||||
|
ref="filmTable"
|
||||||
height="100%"
|
height="100%"
|
||||||
:empty-text="statusText"
|
:empty-text="statusText"
|
||||||
@row-click="(row) => detailEvent(site, row)"
|
@row-click="(row) => detailEvent(site, row)"
|
||||||
@@ -99,7 +136,7 @@
|
|||||||
prop="name"
|
prop="name"
|
||||||
label="片名">
|
label="片名">
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column v-if="type.name === '最新'"
|
||||||
prop="type"
|
prop="type"
|
||||||
label="类型"
|
label="类型"
|
||||||
width="100">
|
width="100">
|
||||||
@@ -120,16 +157,16 @@
|
|||||||
label="语言"
|
label="语言"
|
||||||
width="100">
|
width="100">
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column v-if="showTableLastColumn"
|
||||||
prop="note"
|
|
||||||
label="备注"
|
|
||||||
width="120">
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
prop="last"
|
prop="last"
|
||||||
label="最近更新"
|
label="最近更新"
|
||||||
:formatter="dateFormat"
|
:formatter="dateFormat"
|
||||||
align="left">
|
align="left"
|
||||||
|
width="120">
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
prop="note"
|
||||||
|
label="备注">
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
label="操作"
|
label="操作"
|
||||||
@@ -152,10 +189,10 @@
|
|||||||
</infinite-loading>
|
</infinite-loading>
|
||||||
</el-table>
|
</el-table>
|
||||||
</div>
|
</div>
|
||||||
<div class="show-table" v-show="searchViewMode=== 'table' && show.find">
|
<div class="show-table" v-if="setting.searchViewMode === 'table' && showFind">
|
||||||
<el-table size="mini"
|
<el-table size="mini"
|
||||||
ref="searchResultTable"
|
ref="searchResultTable"
|
||||||
:data="searchContents.filter(res => !setting.excludeR18Films || (res.type !== undefined && !containsR18Keywords(res.type)))"
|
:data="filteredSearchContents"
|
||||||
height="100%"
|
height="100%"
|
||||||
:empty-text="statusText"
|
:empty-text="statusText"
|
||||||
@filter-change="filterChange"
|
@filter-change="filterChange"
|
||||||
@@ -181,36 +218,38 @@
|
|||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
prop="type"
|
prop="type"
|
||||||
:filters="getFilters('type')"
|
|
||||||
:filter-method="(value, row, column) => { this.currentColumn = column; return value === row.type }"
|
|
||||||
label="类型"
|
label="类型"
|
||||||
width="90">
|
width="100">
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
sortable
|
sortable
|
||||||
prop="year"
|
prop="year"
|
||||||
label="上映"
|
label="上映"
|
||||||
width="90">
|
width="100">
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
prop="area"
|
prop="area"
|
||||||
:filters="getFilters('area')"
|
|
||||||
:filter-method="(value, row, column) => { this.currentColumn = column; return value === row.area }"
|
|
||||||
label="地区"
|
label="地区"
|
||||||
width="90">
|
width="100">
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
:filters="getFilters('lang')"
|
:filters="getFilters('lang')"
|
||||||
:filter-method="(value, row, column) => { this.currentColumn = column; return value === row.lang }"
|
:filter-method="(value, row, column) => { this.currentColumn = column; return value === row.lang }"
|
||||||
prop="lang"
|
prop="lang"
|
||||||
label="语言"
|
label="语言"
|
||||||
width="70">
|
width="100">
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column v-if="showTableLastColumn"
|
||||||
|
prop="last"
|
||||||
|
label="最近更新"
|
||||||
|
:formatter="dateFormat"
|
||||||
|
align="left"
|
||||||
|
width="120">
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
sortable
|
sortable
|
||||||
prop="note"
|
prop="note"
|
||||||
label="备注"
|
label="备注">
|
||||||
width="120">
|
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
label="操作"
|
label="操作"
|
||||||
@@ -226,8 +265,8 @@
|
|||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
</div>
|
</div>
|
||||||
<div class="show-picture" v-show="searchViewMode === 'picture' && show.find">
|
<div class="show-picture" v-if="setting.searchViewMode === 'picture' && showFind">
|
||||||
<Waterfall ref="filmSearchWaterfall" :list="searchContents.filter(res => !setting.excludeR18Films || (res.type !== undefined && !containsR18Keywords(res.type)))" :gutter="20" :width="240"
|
<Waterfall ref="filmSearchWaterfall" :list="filteredSearchContents" :gutter="20" :width="240"
|
||||||
:breakpoints="{
|
:breakpoints="{
|
||||||
1200: { //当屏幕宽度小于等于1200
|
1200: { //当屏幕宽度小于等于1200
|
||||||
rowPerView: 4,
|
rowPerView: 4,
|
||||||
@@ -242,7 +281,7 @@
|
|||||||
animationEffect="fadeIn"
|
animationEffect="fadeIn"
|
||||||
backgroundColor="rgba(0, 0, 0, 0)">
|
backgroundColor="rgba(0, 0, 0, 0)">
|
||||||
<template slot="item" slot-scope="props">
|
<template slot="item" slot-scope="props">
|
||||||
<div class="card" v-show="!setting.excludeR18Films || !containsR18Keywords(props.data.type)">
|
<div class="card" v-show="!setting.excludeR18Films || !containsClassFilterKeyword(props.data.type)">
|
||||||
<div class="img">
|
<div class="img">
|
||||||
<div class="site">
|
<div class="site">
|
||||||
<span>{{props.data.site.name}}</span>
|
<span>{{props.data.site.name}}</span>
|
||||||
@@ -277,24 +316,24 @@ import zy from '../lib/site/tools'
|
|||||||
import Waterfall from 'vue-waterfall-plugin'
|
import Waterfall from 'vue-waterfall-plugin'
|
||||||
import InfiniteLoading from 'vue-infinite-loading'
|
import InfiniteLoading from 'vue-infinite-loading'
|
||||||
const { clipboard } = require('electron')
|
const { clipboard } = require('electron')
|
||||||
|
const FILM_DATA_CACHE = {} // key = site.key, value = classList; key = site.key + '@' + type.tid, value = {list, pageCount}
|
||||||
export default {
|
export default {
|
||||||
name: 'film',
|
name: 'film',
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
show: {
|
showFind: false,
|
||||||
body: false,
|
showTableLastColumn: false,
|
||||||
site: false,
|
|
||||||
class: false,
|
|
||||||
classList: false,
|
|
||||||
find: false
|
|
||||||
},
|
|
||||||
sites: [],
|
sites: [],
|
||||||
site: {},
|
site: {},
|
||||||
classList: [],
|
classList: [],
|
||||||
|
searchClassList: [],
|
||||||
type: {},
|
type: {},
|
||||||
selectedClassName: '最新',
|
|
||||||
selectedSiteName: '',
|
selectedSiteName: '',
|
||||||
|
selectedClassName: '',
|
||||||
|
selectedSearchClassNames: [],
|
||||||
|
totalpagecount: 0,
|
||||||
pagecount: 0,
|
pagecount: 0,
|
||||||
|
recordcount: 0,
|
||||||
list: [],
|
list: [],
|
||||||
statusText: ' ',
|
statusText: ' ',
|
||||||
infiniteId: +new Date(),
|
infiniteId: +new Date(),
|
||||||
@@ -302,12 +341,22 @@ export default {
|
|||||||
searchList: [],
|
searchList: [],
|
||||||
searchTxt: '',
|
searchTxt: '',
|
||||||
searchContents: [],
|
searchContents: [],
|
||||||
|
filteredSearchContents: [],
|
||||||
currentColumn: '',
|
currentColumn: '',
|
||||||
searchGroup: '',
|
searchGroup: '',
|
||||||
searchGroups: [],
|
searchGroups: ['站内', '组内', '全站'],
|
||||||
// 福利片关键词
|
classFilterKeywords: [],
|
||||||
r18KeyWords: ['伦理', '论理', '倫理', '福利', '激情', '理论', '写真', '情色', '美女', '街拍', '赤足', '性感', '里番'],
|
filteredList: [],
|
||||||
searchViewMode: 'picture'
|
areas: [],
|
||||||
|
searchRunning: false,
|
||||||
|
siteSearchCount: 0,
|
||||||
|
infiniteHandlerCount: 0,
|
||||||
|
// Toolbar
|
||||||
|
showToolbar: false,
|
||||||
|
selectedAreas: [],
|
||||||
|
sortKeyword: '',
|
||||||
|
sortKeywords: ['按片名', '按上映年份', '按更新时间'],
|
||||||
|
selectedYears: { start: 0, end: new Date().getFullYear() }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
@@ -355,8 +404,22 @@ export default {
|
|||||||
this.SET_SETTING(val)
|
this.SET_SETTING(val)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
DetailCache: {
|
||||||
|
get () {
|
||||||
|
return this.$store.getters.getDetailCache
|
||||||
|
},
|
||||||
|
set (val) {
|
||||||
|
this.SET_DetailCache(val)
|
||||||
|
}
|
||||||
|
},
|
||||||
filterSettings () {
|
filterSettings () {
|
||||||
return this.$store.getters.getSetting.excludeR18Films // 需要监听的数据
|
return this.$store.getters.getSetting.classFilter // 需要监听的数据
|
||||||
|
},
|
||||||
|
searchSites () {
|
||||||
|
if (this.searchGroup === '站内') return [this.site]
|
||||||
|
if (this.searchGroup === '组内') return this.sites.filter(site => site.group === this.site.group)
|
||||||
|
if (this.searchGroup === '全站') return this.sites
|
||||||
|
return this.sites.filter(site => site.isActive)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
filters: {
|
filters: {
|
||||||
@@ -367,7 +430,11 @@ export default {
|
|||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
view () {
|
view () {
|
||||||
this.changeView()
|
if (this.view === 'Film') {
|
||||||
|
this.getAllSites()
|
||||||
|
if (this.$refs.filmWaterfall) this.$refs.filmWaterfall.resize() // 瀑布插件resize和refresh功能相同,只是延时不同
|
||||||
|
if (this.$refs.filmSearchWaterfall) this.$refs.filmSearchWaterfall.resize()
|
||||||
|
}
|
||||||
},
|
},
|
||||||
searchTxt () {
|
searchTxt () {
|
||||||
if (this.searchTxt === '清除历史记录...') {
|
if (this.searchTxt === '清除历史记录...') {
|
||||||
@@ -377,14 +444,110 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
filterSettings () {
|
filterSettings () {
|
||||||
|
this.refreshClass()
|
||||||
|
},
|
||||||
|
list: {
|
||||||
|
handler (list) {
|
||||||
|
this.areas = [...new Set(list.map(ele => ele.area))].filter(x => x)
|
||||||
|
this.refreshFilteredList()
|
||||||
|
},
|
||||||
|
deep: true
|
||||||
|
},
|
||||||
|
siteSearchCount () {
|
||||||
|
if (this.siteSearchCount === this.searchSites.length) this.searchRunning = false
|
||||||
|
},
|
||||||
|
site () {
|
||||||
this.siteClick(this.site.name)
|
this.siteClick(this.site.name)
|
||||||
|
},
|
||||||
|
searchContents: {
|
||||||
|
handler (list) {
|
||||||
|
list = list.filter(res => !this.setting.excludeR18Films || !this.containsClassFilterKeyword(res.type))
|
||||||
|
this.areas = [...new Set(list.map(ele => ele.area))].filter(x => x)
|
||||||
|
this.searchClassList = [...new Set(list.map(ele => ele.type))].filter(x => x)
|
||||||
|
this.refreshFilteredList()
|
||||||
|
},
|
||||||
|
deep: true
|
||||||
|
},
|
||||||
|
selectedAreas: {
|
||||||
|
handler () {
|
||||||
|
this.infiniteHandlerCount = 0
|
||||||
|
},
|
||||||
|
deep: true
|
||||||
|
},
|
||||||
|
selectedYears: {
|
||||||
|
handler () {
|
||||||
|
this.infiniteHandlerCount = 0
|
||||||
|
},
|
||||||
|
deep: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapMutations(['SET_VIEW', 'SET_DETAIL', 'SET_VIDEO', 'SET_SHARE', 'SET_SETTING']),
|
...mapMutations(['SET_VIEW', 'SET_DETAIL', 'SET_VIDEO', 'SET_SHARE', 'SET_SETTING', 'SET_DetailCache']),
|
||||||
updateSearchViewMode () {
|
backTop () {
|
||||||
|
const viewMode = this.showFind ? this.setting.searchViewMode : this.setting.view
|
||||||
|
if (viewMode === 'picture') {
|
||||||
|
document.getElementById('film-body').scrollTop = 0
|
||||||
|
} else {
|
||||||
|
const table = this.showFind ? this.$refs.searchResultTable : this.$refs.filmTable
|
||||||
|
table.bodyWrapper.scrollTop = 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
refreshFilteredList () {
|
||||||
|
if (!this.showToolbar) {
|
||||||
|
this.sortKeyword = ''
|
||||||
|
this.selectedAreas = []
|
||||||
|
this.selectedSearchClassNames = []
|
||||||
|
this.selectedYears.start = 0
|
||||||
|
this.selectedYears.end = new Date().getFullYear()
|
||||||
|
}
|
||||||
|
let filteredData = this.showFind ? this.searchContents : this.list
|
||||||
|
if (this.showFind) filteredData = filteredData.filter(x => (this.selectedSearchClassNames.length === 0) || this.selectedSearchClassNames.includes(x.type))
|
||||||
|
filteredData = filteredData.filter(x => (this.selectedAreas.length === 0) || this.selectedAreas.includes(x.area))
|
||||||
|
filteredData = filteredData.filter(res => !this.setting.excludeR18Films || !this.containsClassFilterKeyword(res.type))
|
||||||
|
filteredData = filteredData.filter(res => res.year >= this.selectedYears.start)
|
||||||
|
filteredData = filteredData.filter(res => res.year <= this.selectedYears.end)
|
||||||
|
if (!this.showFind) this.selectedClassName = this.type.name + ' ' + filteredData.length + '/' + this.recordcount
|
||||||
|
switch (this.sortKeyword) {
|
||||||
|
case '按上映年份':
|
||||||
|
filteredData.sort(function (a, b) {
|
||||||
|
return b.year - a.year
|
||||||
|
})
|
||||||
|
break
|
||||||
|
case '按片名':
|
||||||
|
filteredData.sort(function (a, b) {
|
||||||
|
return a.name.localeCompare(b.name, 'zh')
|
||||||
|
})
|
||||||
|
break
|
||||||
|
case '按更新时间':
|
||||||
|
filteredData.sort(function (a, b) {
|
||||||
|
return new Date(b.last) - new Date(a.last)
|
||||||
|
})
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
filteredData.sort(function (a, b) {
|
||||||
|
return new Date(b.last) - new Date(a.last)
|
||||||
|
})
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get unique film data
|
||||||
|
filteredData = Array.from(new Set(filteredData))
|
||||||
|
if (this.showFind) {
|
||||||
|
this.filteredSearchContents = filteredData
|
||||||
|
} else {
|
||||||
|
this.filteredList = filteredData
|
||||||
|
}
|
||||||
|
},
|
||||||
|
toggleViewMode () {
|
||||||
|
if (this.showFind) {
|
||||||
|
this.setting.searchViewMode = this.setting.searchViewMode === 'picture' ? 'table' : 'picture'
|
||||||
|
setTimeout(() => { if (this.$refs.filmSearchWaterfall) this.$refs.filmSearchWaterfall.refresh() }, 700)
|
||||||
|
} else {
|
||||||
|
this.setting.view = this.setting.view === 'picture' ? 'table' : 'picture'
|
||||||
|
}
|
||||||
setting.find().then(res => {
|
setting.find().then(res => {
|
||||||
res.searchViewMode = this.searchViewMode
|
res.searchViewMode = this.setting.searchViewMode
|
||||||
|
res.view = this.setting.view
|
||||||
setting.update(res)
|
setting.update(res)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
@@ -392,16 +555,15 @@ export default {
|
|||||||
return a.localeCompare(b, 'zh')
|
return a.localeCompare(b, 'zh')
|
||||||
},
|
},
|
||||||
dateFormat (row, column) {
|
dateFormat (row, column) {
|
||||||
var date = row[column.property]
|
const date = row[column.property]
|
||||||
if (date === undefined) {
|
if (date === undefined) {
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
return date.split(/\s/)[0]
|
return date.split(/\s/)[0]
|
||||||
},
|
},
|
||||||
getFilters (column) {
|
getFilters (column) {
|
||||||
const searchContents = this.searchContents.filter(res => !this.setting.excludeR18Films || (res.type !== undefined && !this.containsR18Keywords(res.type)))
|
if (column === 'siteName') return [...new Set(this.filteredSearchContents.map(row => row.site.name))].map(e => { return { text: e, value: e } }) // 有方法合并这两行吗?
|
||||||
if (column === 'siteName') return [...new Set(searchContents.map(row => row.site.name))].map(e => { return { text: e, value: e } }) // 有方法合并这两行吗?
|
return [...new Set(this.filteredSearchContents.map(row => row[column]))].map(e => { return { text: e, value: e } })
|
||||||
return [...new Set(searchContents.map(row => row[column]))].map(e => { return { text: e, value: e } })
|
|
||||||
},
|
},
|
||||||
filterChange (filters) {
|
filterChange (filters) {
|
||||||
// 一次只能一列
|
// 一次只能一列
|
||||||
@@ -409,7 +571,7 @@ export default {
|
|||||||
const otherColumns = this.$refs.searchResultTable.columns.filter(col => col.id !== this.currentColumn.id)
|
const otherColumns = this.$refs.searchResultTable.columns.filter(col => col.id !== this.currentColumn.id)
|
||||||
otherColumns.forEach(col => { col.filterable = false })
|
otherColumns.forEach(col => { col.filterable = false })
|
||||||
} else {
|
} else {
|
||||||
const filterLabels = ['源站', '类型', '地区', '语言']
|
const filterLabels = ['源站', '语言']
|
||||||
const columns = this.$refs.searchResultTable.columns.filter(col => filterLabels.includes(col.label))
|
const columns = this.$refs.searchResultTable.columns.filter(col => filterLabels.includes(col.label))
|
||||||
columns.forEach(col => { col.filterable = true })
|
columns.forEach(col => { col.filterable = true })
|
||||||
}
|
}
|
||||||
@@ -417,111 +579,141 @@ export default {
|
|||||||
siteClick (siteName) {
|
siteClick (siteName) {
|
||||||
this.list = []
|
this.list = []
|
||||||
this.site = this.sites.find(x => x.name === siteName)
|
this.site = this.sites.find(x => x.name === siteName)
|
||||||
if (this.searchTxt.length > 0 && this.searchGroup === '站内') {
|
if (this.searchGroup === '站内' && this.searchTxt) {
|
||||||
this.searchEvent()
|
this.searchEvent()
|
||||||
|
return
|
||||||
} else {
|
} else {
|
||||||
this.searchTxt = ''
|
this.searchTxt = ''
|
||||||
this.show.find = false
|
}
|
||||||
this.classList = []
|
this.showFind = false
|
||||||
this.type = {}
|
this.classList = []
|
||||||
|
if (FILM_DATA_CACHE[this.site.key]) {
|
||||||
|
this.classList = FILM_DATA_CACHE[this.site.key].classList
|
||||||
|
this.classClick(this.type.name)
|
||||||
|
} else {
|
||||||
this.getClass().then(res => {
|
this.getClass().then(res => {
|
||||||
this.classClick(this.classList[0].name)
|
this.classList = res
|
||||||
|
// cache classList data
|
||||||
|
FILM_DATA_CACHE[this.site.key] = {
|
||||||
|
classList: this.classList
|
||||||
|
}
|
||||||
|
this.classClick(this.type.name)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
refreshClass () {
|
||||||
|
this.getClass().then(res => {
|
||||||
|
this.classList = res
|
||||||
|
// cache classList data
|
||||||
|
FILM_DATA_CACHE[this.site.key] = {
|
||||||
|
classList: this.classList
|
||||||
|
}
|
||||||
|
this.classClick(this.type.name)
|
||||||
|
})
|
||||||
|
},
|
||||||
classClick (className) {
|
classClick (className) {
|
||||||
this.show.classList = false
|
|
||||||
this.list = []
|
this.list = []
|
||||||
this.type = this.classList.find(x => x.name === className)
|
this.type = this.classList.find(x => x.name === className)
|
||||||
this.getPage().then(res => {
|
this.infiniteHandlerCount = 0
|
||||||
if (res) {
|
if (!this.type) {
|
||||||
|
this.type = this.classList[0]
|
||||||
|
}
|
||||||
|
if (this.type.name.endsWith('剧')) this.selectedAreas = []
|
||||||
|
const cacheKey = this.site.key + '@' + this.type.tid
|
||||||
|
if (FILM_DATA_CACHE[cacheKey]) {
|
||||||
|
this.totalpagecount = FILM_DATA_CACHE[cacheKey].totalpagecount
|
||||||
|
this.pagecount = FILM_DATA_CACHE[cacheKey].pagecount
|
||||||
|
this.recordcount = FILM_DATA_CACHE[cacheKey].recordcount
|
||||||
|
this.list = FILM_DATA_CACHE[cacheKey].list
|
||||||
|
this.areas = FILM_DATA_CACHE[cacheKey].areas
|
||||||
|
} else {
|
||||||
|
zy.page(this.site.key, this.type.tid).then(res => {
|
||||||
|
this.totalpagecount = res.pagecount
|
||||||
|
this.pagecount = res.pagecount
|
||||||
|
this.recordcount = res.recordcount
|
||||||
this.infiniteId += 1
|
this.infiniteId += 1
|
||||||
}
|
})
|
||||||
})
|
}
|
||||||
},
|
},
|
||||||
getClass () {
|
getClass () {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const key = this.site.key
|
const key = this.site.key
|
||||||
// 屏蔽主分类
|
|
||||||
const classToHide = ['电影', '电影片', '电视剧', '连续剧', '综艺', '动漫']
|
|
||||||
zy.class(key).then(res => {
|
zy.class(key).then(res => {
|
||||||
var allClass = [{ name: '最新', tid: 0 }]
|
const allClass = [{ name: '最新', tid: 0 }]
|
||||||
res.class.forEach(element => {
|
res.class.forEach(element => {
|
||||||
if (!this.setting.excludeRootClasses || !classToHide.includes(element.name)) {
|
if (!this.containsClassFilterKeyword(element.name)) {
|
||||||
if (this.setting.excludeR18Films) {
|
allClass.push(element)
|
||||||
const containKeyWord = this.containsR18Keywords(element.name)
|
|
||||||
if (!containKeyWord) {
|
|
||||||
allClass.push(element)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
allClass.push(element)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
this.classList = allClass
|
resolve(allClass)
|
||||||
this.show.class = true
|
|
||||||
this.pagecount = res.pagecount
|
|
||||||
this.type = this.classList[0]
|
|
||||||
resolve(true)
|
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
reject(err)
|
reject(err)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
containsR18Keywords (name) {
|
containsClassFilterKeyword (name) {
|
||||||
var containKeyWord = false
|
let ret = false
|
||||||
if (!name) {
|
// 主分类过滤, 检测关键词是否包含分类名
|
||||||
return containKeyWord
|
if (this.setting.excludeRootClasses) {
|
||||||
|
ret = this.setting.rootClassFilter?.some(v => v.includes(name))
|
||||||
}
|
}
|
||||||
return this.r18KeyWords.some(v => name.includes(v))
|
// 福利过滤,检测分类名是否包含关键词
|
||||||
|
if (this.setting.excludeR18Films && !ret) {
|
||||||
|
ret = this.setting.r18ClassFilter?.some(v => name?.includes(v))
|
||||||
|
}
|
||||||
|
return ret
|
||||||
},
|
},
|
||||||
getPage () {
|
toFlipPagecount () {
|
||||||
return new Promise((resolve, reject) => {
|
return this.site.reverseOrder
|
||||||
const key = this.site.key
|
|
||||||
const type = this.type.tid
|
|
||||||
zy.page(key, type).then(res => {
|
|
||||||
this.pagecount = res.pagecount
|
|
||||||
this.show.body = true
|
|
||||||
resolve(true)
|
|
||||||
}).catch(err => {
|
|
||||||
reject(err)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
infiniteHandler ($state) {
|
infiniteHandler ($state) {
|
||||||
const key = this.site.key
|
const key = this.site.key
|
||||||
const type = this.type.tid
|
const typeTid = this.type.tid
|
||||||
const page = this.pagecount
|
let page = this.pagecount
|
||||||
|
if (this.toFlipPagecount()) {
|
||||||
|
page = this.totalpagecount - this.pagecount + 1
|
||||||
|
}
|
||||||
this.statusText = ' '
|
this.statusText = ' '
|
||||||
if (key === undefined || page < 1 || type === undefined) { // OK资源前几类硬是去不掉
|
if (key === undefined || page < 1 || page > this.totalpagecount || typeTid === undefined) {
|
||||||
$state.complete()
|
$state.complete()
|
||||||
this.statusText = '暂无数据'
|
this.statusText = '暂无数据'
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
zy.list(key, page, type).then(res => {
|
if (this.showToolbar && this.filteredList.length && this.filteredList.length < 10) {
|
||||||
if (res) {
|
this.infiniteHandlerCount++
|
||||||
this.pagecount -= 1
|
}
|
||||||
const type = Object.prototype.toString.call(res)
|
const interval = this.setting.view === 'picture' ? 1200 : 300
|
||||||
if (type === '[object Undefined]') {
|
setTimeout(() => {
|
||||||
$state.complete()
|
zy.list(key, page, typeTid).then(res => {
|
||||||
|
if (res) {
|
||||||
|
this.pagecount -= 1
|
||||||
|
const type = Object.prototype.toString.call(res)
|
||||||
|
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())
|
||||||
|
} else {
|
||||||
|
// 如果是需要解析的视频网站,zy.list已经是按从新到旧排列
|
||||||
|
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()
|
||||||
|
// 更新缓存数据
|
||||||
|
const cacheKey = this.site.key + '@' + typeTid
|
||||||
|
FILM_DATA_CACHE[cacheKey] = {
|
||||||
|
pagecount: this.pagecount,
|
||||||
|
recordcount: this.recordcount,
|
||||||
|
list: this.list
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (type === '[object Array]') {
|
})
|
||||||
// zy.list 返回的是按时间从旧到新排列, 我门需要翻转为从新到旧
|
}, (this.infiniteHandlerCount <= 1 ? 0 : this.infiniteHandlerCount - 1) * interval)
|
||||||
this.list.push(...res.reverse())
|
|
||||||
}
|
|
||||||
if (type === '[object Object]') {
|
|
||||||
this.list.push(res)
|
|
||||||
}
|
|
||||||
$state.loaded()
|
|
||||||
// 数据更新后,刷新页面
|
|
||||||
if (this.$refs.filmWaterfall) {
|
|
||||||
this.$refs.filmWaterfall.refresh()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$state.complete()
|
|
||||||
this.statusText = '暂无数据'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
detailEvent (site, e) {
|
detailEvent (site, e) {
|
||||||
this.detail = {
|
this.detail = {
|
||||||
@@ -538,9 +730,6 @@ export default {
|
|||||||
} else {
|
} else {
|
||||||
this.video = { key: site.key, info: { id: e.id, name: e.name, index: 0, site: site } }
|
this.video = { key: site.key, info: { id: e.id, name: e.name, index: 0, site: site } }
|
||||||
}
|
}
|
||||||
zy.detail(site.key, e.id).then(detailRes => {
|
|
||||||
this.video.detail = detailRes
|
|
||||||
})
|
|
||||||
this.view = 'Play'
|
this.view = 'Play'
|
||||||
},
|
},
|
||||||
async starEvent (site, e) {
|
async starEvent (site, e) {
|
||||||
@@ -548,17 +737,19 @@ export default {
|
|||||||
if (db) {
|
if (db) {
|
||||||
this.$message.info('已存在')
|
this.$message.info('已存在')
|
||||||
} else {
|
} else {
|
||||||
zy.detail(site.key, e.id).then(detailRes => {
|
const cacheKey = site.key + '@' + e.id
|
||||||
const docs = {
|
if (!this.DetailCache[cacheKey]) {
|
||||||
key: site.key,
|
this.DetailCache[cacheKey] = await zy.detail(site.key, e.id)
|
||||||
ids: e.id,
|
}
|
||||||
site: site,
|
const docs = {
|
||||||
name: e.name,
|
key: site.key,
|
||||||
detail: detailRes
|
ids: e.id,
|
||||||
}
|
site: site,
|
||||||
star.add(docs).then(res => {
|
name: e.name,
|
||||||
this.$message.success('收藏成功')
|
detail: this.DetailCache[cacheKey]
|
||||||
})
|
}
|
||||||
|
star.add(docs).then(res => {
|
||||||
|
this.$message.success('收藏成功')
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -569,58 +760,20 @@ export default {
|
|||||||
info: e
|
info: e
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
downloadEvent (site, row) {
|
async downloadEvent (site, row) {
|
||||||
zy.download(site.key, row.id).then(res => {
|
const db = await history.find({ site: site.key, ids: row.id })
|
||||||
if (res && res.length > 0) {
|
let videoFlag
|
||||||
const text = res.m3u8List
|
if (db) videoFlag = db.videoFlag
|
||||||
if (text) {
|
zy.download(site.key, row.id, videoFlag).then(res => {
|
||||||
const list = text.split('#')
|
clipboard.writeText(res.downloadUrls)
|
||||||
let downloadUrl = res.name + '\n'
|
this.$message.success(res.info)
|
||||||
for (const i of list) {
|
}).catch((err) => {
|
||||||
const url = encodeURI(i.split('$')[1])
|
this.$message.error(err.info)
|
||||||
downloadUrl += (url + '\n')
|
|
||||||
}
|
|
||||||
clipboard.writeText(downloadUrl)
|
|
||||||
this.$message.success('『MP4』格式的链接已复制, 快去下载吧!')
|
|
||||||
} else {
|
|
||||||
this.$message.warning('没有查询到下载链接.')
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let m3u8List = []
|
|
||||||
const dd = row.dl.dd
|
|
||||||
const type = Object.prototype.toString.call(dd)
|
|
||||||
if (type === '[object Array]') {
|
|
||||||
for (const i of dd) {
|
|
||||||
if (i._flag.indexOf('m3u8') >= 0) {
|
|
||||||
m3u8List = i._t.split('#')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
m3u8List = dd._t.split('#')
|
|
||||||
}
|
|
||||||
let downloadUrl = row.name + '\n'
|
|
||||||
for (const i of m3u8List) {
|
|
||||||
const url = encodeURI(i.split('$')[1])
|
|
||||||
downloadUrl += (url + '\n')
|
|
||||||
}
|
|
||||||
clipboard.writeText(downloadUrl)
|
|
||||||
this.$message.success('『M3U8』格式的链接已复制, 快去下载吧!')
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
changeView () {
|
|
||||||
if (this.view === 'Film') {
|
|
||||||
this.getAllSites()
|
|
||||||
if (this.setting.view === 'picture') {
|
|
||||||
if (this.$refs.filmWaterfall) {
|
|
||||||
this.$refs.filmWaterfall.refresh()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
querySearch (queryString, cb) {
|
querySearch (queryString, cb) {
|
||||||
var searchList = this.searchList.slice(0, -1)
|
const searchList = this.searchList.slice(0, -1)
|
||||||
var results = queryString ? searchList.filter(this.createFilter(queryString)) : this.searchList
|
const results = queryString ? searchList.filter(this.createFilter(queryString)) : this.searchList
|
||||||
// 调用 callback 返回建议列表的数据
|
// 调用 callback 返回建议列表的数据
|
||||||
cb(results)
|
cb(results)
|
||||||
},
|
},
|
||||||
@@ -651,6 +804,9 @@ export default {
|
|||||||
this.searchList.push({ id: this.searchList.length + 1, keywords: '清除历史记录...' })
|
this.searchList.push({ id: this.searchList.length + 1, keywords: '清除历史记录...' })
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
stopSearchEvent () {
|
||||||
|
this.searchRunning = false
|
||||||
|
},
|
||||||
searchEvent () {
|
searchEvent () {
|
||||||
const wd = this.searchTxt
|
const wd = this.searchTxt
|
||||||
if (this.setting.searchGroup !== this.searchGroup) {
|
if (this.setting.searchGroup !== this.searchGroup) {
|
||||||
@@ -659,79 +815,91 @@ export default {
|
|||||||
}
|
}
|
||||||
if (!wd) return
|
if (!wd) return
|
||||||
this.searchID += 1
|
this.searchID += 1
|
||||||
var searchSites = []
|
|
||||||
if (this.searchGroup === '站内') searchSites.push(this.site)
|
|
||||||
if (this.searchGroup === '全站') searchSites = this.sites
|
|
||||||
if (!searchSites.length) {
|
|
||||||
searchSites = this.sites.filter(site => site.group === this.searchGroup)
|
|
||||||
}
|
|
||||||
this.searchContents = []
|
this.searchContents = []
|
||||||
this.pagecount = 0
|
this.showFind = true
|
||||||
this.show.find = true
|
|
||||||
this.show.class = false
|
|
||||||
this.statusText = ' '
|
this.statusText = ' '
|
||||||
if (wd) {
|
this.searchRunning = true
|
||||||
searchSites.forEach(site => {
|
this.siteSearchCount = 0
|
||||||
const id = this.searchID
|
this.searchSites.forEach(site => {
|
||||||
zy.search(site.key, wd).then(res => {
|
const id = this.searchID
|
||||||
if (id !== this.searchID) return
|
zy.search(site.key, wd).then(res => {
|
||||||
const type = Object.prototype.toString.call(res)
|
if (id !== this.searchID || !this.searchRunning) return
|
||||||
if (type === '[object Array]') {
|
const type = Object.prototype.toString.call(res)
|
||||||
res.forEach(element => {
|
if (type === '[object Array]') {
|
||||||
zy.detail(site.key, element.id).then(detailRes => {
|
let count = 0
|
||||||
detailRes.site = site
|
res.forEach(element => {
|
||||||
if (id !== this.searchID) return
|
zy.detail(site.key, element.id).then(detailRes => {
|
||||||
|
if (id !== this.searchID || !this.searchRunning) return
|
||||||
|
detailRes.site = site
|
||||||
|
if (this.isValidSearchResult(detailRes)) {
|
||||||
this.searchContents.push(detailRes)
|
this.searchContents.push(detailRes)
|
||||||
this.searchContents.sort(function (a, b) {
|
this.searchContents.sort(function (a, b) {
|
||||||
return a.site.id - b.site.id
|
return a.site.id - b.site.id
|
||||||
})
|
})
|
||||||
this.statusText = '暂无数据'
|
}
|
||||||
})
|
}).finally(() => { count++; if (count === res.length) { this.siteSearchCount++; this.statusText = '暂无数据' } })
|
||||||
})
|
})
|
||||||
}
|
} else if (type === '[object Object]') {
|
||||||
if (type === '[object Object]') {
|
zy.detail(site.key, res.id).then(detailRes => {
|
||||||
zy.detail(site.key, res.id).then(detailRes => {
|
if (id !== this.searchID || !this.searchRunning) return
|
||||||
detailRes.site = site
|
detailRes.site = site
|
||||||
if (id !== this.searchID) return
|
if (this.isValidSearchResult(detailRes)) {
|
||||||
this.searchContents.push(detailRes)
|
this.searchContents.push(detailRes)
|
||||||
this.searchContents.sort(function (a, b) {
|
this.searchContents.sort(function (a, b) {
|
||||||
return a.site.id - b.site.id
|
return a.site.id - b.site.id
|
||||||
})
|
})
|
||||||
this.statusText = '暂无数据'
|
}
|
||||||
})
|
}).finally(() => { this.siteSearchCount++; this.statusText = '暂无数据' })
|
||||||
}
|
} else if (res === undefined) {
|
||||||
})
|
this.siteSearchCount++
|
||||||
})
|
this.statusText = '暂无数据'
|
||||||
}
|
if (this.searchGroup === '站内') this.$message.info('没有查询到数据!')
|
||||||
|
}
|
||||||
|
}).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 () {
|
searchAndRecord () {
|
||||||
this.addSearchRecord()
|
this.addSearchRecord()
|
||||||
this.searchEvent()
|
this.searchEvent()
|
||||||
},
|
},
|
||||||
searchChangeEvent () {
|
searchChangeEvent () {
|
||||||
if (this.searchTxt.length >= 1) {
|
if (!this.searchTxt.length) {
|
||||||
this.show.class = false
|
|
||||||
} else {
|
|
||||||
this.show.class = true
|
|
||||||
this.searchContents = []
|
this.searchContents = []
|
||||||
this.show.find = false
|
this.showFind = false
|
||||||
if (this.setting.view === 'picture' && this.$refs.filmWaterfall) {
|
|
||||||
this.$refs.filmWaterfall.refresh()
|
|
||||||
} else {
|
|
||||||
this.getClass().then(res => {
|
|
||||||
if (res) {
|
|
||||||
this.infiniteId += 1
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
async getDefaultSites () {
|
||||||
|
const s = await setting.find()
|
||||||
|
zy.getDefaultSites(s.sitesDataURL).then(res => {
|
||||||
|
if (res && typeof res === 'string') {
|
||||||
|
const json = JSON.parse(res)
|
||||||
|
sites.clear().then(sites.bulkAdd(json))
|
||||||
|
}
|
||||||
|
if (res && typeof res === 'object') {
|
||||||
|
sites.clear().then(sites.bulkAdd(res))
|
||||||
|
}
|
||||||
|
sites.all().then(res => {
|
||||||
|
if (res) {
|
||||||
|
this.sites = res.filter(item => item.isActive)
|
||||||
|
if (this.site === undefined || !this.sites.some(x => x.key === this.site.key)) {
|
||||||
|
this.site = this.sites[0]
|
||||||
|
this.selectedSiteName = this.sites[0].name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}).catch(error => {
|
||||||
|
this.$message.error('获取云端源站失败. ' + error)
|
||||||
|
})
|
||||||
|
},
|
||||||
getAllSites () {
|
getAllSites () {
|
||||||
sites.all().then(res => {
|
sites.all().then(res => {
|
||||||
if (res.length <= 0) {
|
if (res.length <= 0) {
|
||||||
this.site = {}
|
this.$message.warning('检测到视频源未能正常加载, 即将重置源.')
|
||||||
this.type = {}
|
this.getDefaultSites()
|
||||||
this.list = []
|
|
||||||
} else {
|
} else {
|
||||||
this.sites = res.filter(item => item.isActive)
|
this.sites = res.filter(item => item.isActive)
|
||||||
if (this.site === undefined || !this.sites.some(x => x.key === this.site.key)) {
|
if (this.site === undefined || !this.sites.some(x => x.key === this.site.key)) {
|
||||||
@@ -739,35 +907,23 @@ export default {
|
|||||||
this.selectedSiteName = this.sites[0].name
|
this.selectedSiteName = this.sites[0].name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.searchGroups = [...new Set(this.sites.map(site => site.group))]
|
|
||||||
if (this.searchGroups.length === 1) this.searchGroups = []
|
|
||||||
this.searchGroups.unshift('站内')
|
|
||||||
this.searchGroups.push('全站')
|
|
||||||
this.searchGroup = this.setting.searchGroup
|
this.searchGroup = this.setting.searchGroup
|
||||||
if (this.searchGroup === undefined) setting.find().then(res => { this.searchGroup = res.searchGroup })
|
if (this.searchGroup === undefined) setting.find().then(res => { this.searchGroup = res.searchGroup })
|
||||||
})
|
})
|
||||||
},
|
|
||||||
getSearchViewMode () {
|
|
||||||
setting.find().then(res => {
|
|
||||||
this.searchViewMode = res.searchViewMode === undefined ? 'picture' : res.searchViewMode
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created () {
|
created () {
|
||||||
this.getAllSites()
|
this.getAllSites()
|
||||||
this.getSearchHistory()
|
this.getSearchHistory()
|
||||||
this.getSearchViewMode()
|
|
||||||
},
|
},
|
||||||
mounted () {
|
mounted () {
|
||||||
window.addEventListener('resize', () => {
|
addEventListener('resize', () => {
|
||||||
if (this.$refs.filmWaterfall && this.view === 'Film') {
|
setTimeout(() => {
|
||||||
this.$refs.filmWaterfall.resize()
|
this.showTableLastColumn = window.outerWidth >= 1200
|
||||||
this.$refs.filmWaterfall.refresh()
|
if (this.$refs.filmWaterfall) this.$refs.filmWaterfall.resize()
|
||||||
setTimeout(() => {
|
if (this.$refs.filmSearchWaterfall) this.$refs.filmSearchWaterfall.resize()
|
||||||
this.$refs.filmWaterfall.refresh()
|
}, 500)
|
||||||
}, 500)
|
})
|
||||||
}
|
|
||||||
}, false)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
const { remote } = require('electron')
|
const remote = require('@electron/remote')
|
||||||
export default {
|
export default {
|
||||||
name: 'frame',
|
name: 'frame',
|
||||||
computed: {
|
computed: {
|
||||||
|
|||||||
@@ -1,14 +1,63 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="listpage" id="history">
|
<div class="listpage" id="history">
|
||||||
<div class="listpage-header" id="history-header">
|
<div class="listpage-header" id="history-header">
|
||||||
<el-switch v-model="viewMode" active-text="海报" active-value="picture" inactive-text="列表" inactive-value="table" @change="updateViewMode"></el-switch>
|
<el-button @click.stop="exportHistory" icon="el-icon-upload2" title="导出全部,自动添加扩展名">导出</el-button>
|
||||||
<el-button @click.stop="exportHistory" icon="el-icon-upload2">导出</el-button>
|
<el-button @click.stop="importHistory" icon="el-icon-download" title="支持同时导入多个文件">导入</el-button>
|
||||||
<el-button @click.stop="importHistory" icon="el-icon-download">导入</el-button>
|
<el-button @click.stop="removeSelectedItems" icon="el-icon-delete-solid">{{ multipleSelection.length === 0 ? "清空" : "删除所选" }}</el-button>
|
||||||
<el-button @click.stop="clearAllHistory" icon="el-icon-delete-solid">清空</el-button>
|
<b-button-group>
|
||||||
|
<el-switch v-model="onlyShowItemsHasUpdate" active-text="有更新" inactive-text="全部" @change="refreshFilteredList"></el-switch>
|
||||||
|
<el-button @click.stop="updateAllEvent" icon="el-icon-refresh">检查更新</el-button>
|
||||||
|
</b-button-group>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="toolbar" v-show="showToolbar">
|
||||||
|
<el-select v-model="selectedAreas" size="small" multiple placeholder="地区" popper-class="popper" :popper-append-to-body="false" @remove-tag="refreshFilteredList" @change="refreshFilteredList">
|
||||||
|
<el-option
|
||||||
|
v-for="item in areas"
|
||||||
|
:key="item"
|
||||||
|
:label="item"
|
||||||
|
:value="item">
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
<el-select v-model="selectedTypes" size="small" multiple placeholder="类型" popper-class="popper" :popper-append-to-body="false" @remove-tag="refreshFilteredList" @change="refreshFilteredList">
|
||||||
|
<el-option
|
||||||
|
v-for="item in types"
|
||||||
|
:key="item"
|
||||||
|
:label="item"
|
||||||
|
:value="item">
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
<el-select v-model="sortKeyword" size="small" placeholder="排序" popper-class="popper" :popper-append-to-body="false" @change="refreshFilteredList">
|
||||||
|
<el-option
|
||||||
|
v-for="item in sortKeywords"
|
||||||
|
:key="item"
|
||||||
|
:label="item"
|
||||||
|
:value="item">
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
<span>
|
||||||
|
上映区间:
|
||||||
|
<el-input-number size="small" v-model="selectedYears.start" :min=0 :max="new Date().getFullYear()" controls-position="right" step-strictly @change="refreshFilteredList"></el-input-number>
|
||||||
|
至
|
||||||
|
<el-input-number size="small" v-model="selectedYears.end" :min=0 :max="new Date().getFullYear()" controls-position="right" step-strictly @change="refreshFilteredList"></el-input-number>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<el-divider class="listpage-header-divider" content-position="right">
|
||||||
|
<el-button type="text" size="mini" @click="toggleViewMode">视图切换</el-button>
|
||||||
|
<el-button type="text" size="mini" @click='() => { showToolbar = !showToolbar; if (!showToolbar) this.refreshFilteredList() }' title="收起工具栏会重置筛选排序">{{ showToolbar ? '隐藏工具栏' : '显示工具栏' }}</el-button>
|
||||||
|
<el-button type="text" size="mini" @click="backTop">回到顶部</el-button>
|
||||||
|
</el-divider>
|
||||||
<div class="listpage-body" id="history-body">
|
<div class="listpage-body" id="history-body">
|
||||||
<div class="show-table" id="history-table" v-show="viewMode === 'table'">
|
<div class="show-table" id="history-table" v-if="setting.historyViewMode === 'table'">
|
||||||
<el-table size="mini" fit height="100%" :data="history" row-key="id" @row-click="detailEvent">
|
<el-table size="mini" fit height="100%"
|
||||||
|
:data="filteredList"
|
||||||
|
row-key="id"
|
||||||
|
ref="historyTable"
|
||||||
|
@select="selectionCellClick"
|
||||||
|
@selection-change="handleSelectionChange"
|
||||||
|
@row-click="detailEvent">
|
||||||
|
<el-table-column
|
||||||
|
type="selection">
|
||||||
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
prop="name"
|
prop="name"
|
||||||
label="片名">
|
label="片名">
|
||||||
@@ -26,16 +75,17 @@
|
|||||||
width="180"
|
width="180"
|
||||||
label="观看至">
|
label="观看至">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<span v-if="scope.row.detail && scope.row.detail.m3u8List && scope.row.detail.m3u8List.length > 1">
|
<span v-if="scope.row.detail && scope.row.detail.fullList[0].list && scope.row.detail.fullList[0].list.length > 1">
|
||||||
第{{ scope.row.index + 1 }}集(共{{scope.row.detail.m3u8List.length}}集)
|
第{{ scope.row.index + 1 }}集(共{{scope.row.detail.fullList[0].list.length}}集)
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column v-if="history.some(e => e.time)"
|
<el-table-column v-if="list.some(e => e.time)"
|
||||||
width="150"
|
width="200"
|
||||||
label="时间进度">
|
label="时间进度">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<span v-if="scope.row.time && scope.row.duration">{{fmtMSS(scope.row.time.toFixed(0))}}/{{fmtMSS(scope.row.duration.toFixed(0))}}</span>
|
<span v-if="scope.row.time && scope.row.duration">{{fmtMSS(scope.row.time.toFixed(0))}}/{{fmtMSS(scope.row.duration.toFixed(0))}} ({{progress(scope.row)}}%)</span>
|
||||||
|
<span v-if="scope.row.onlinePlay">在线解析</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
@@ -52,8 +102,8 @@
|
|||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
</div>
|
</div>
|
||||||
<div class="show-picture" id="star-picture" v-show="viewMode === 'picture'">
|
<div class="show-picture" id="star-picture" v-if="setting.historyViewMode === 'picture'">
|
||||||
<Waterfall ref="historyWaterfall" :list="history" :gutter="20" :width="240"
|
<Waterfall ref="historyWaterfall" :list="filteredList" :gutter="20" :width="240"
|
||||||
:breakpoints="{
|
:breakpoints="{
|
||||||
1200: { //当屏幕宽度小于等于1200
|
1200: { //当屏幕宽度小于等于1200
|
||||||
rowPerView: 4,
|
rowPerView: 4,
|
||||||
@@ -70,6 +120,9 @@
|
|||||||
<template slot="item" slot-scope="props">
|
<template slot="item" slot-scope="props">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="img">
|
<div class="img">
|
||||||
|
<div class="update" v-if="props.data.hasUpdate">
|
||||||
|
<span>有更新</span>
|
||||||
|
</div>
|
||||||
<img v-if="props.data.detail && props.data.detail.pic" style="width: 100%" :src="props.data.detail.pic" alt="" @load="$refs.historyWaterfall.refresh()" @click="detailEvent(props.data)">
|
<img v-if="props.data.detail && props.data.detail.pic" style="width: 100%" :src="props.data.detail.pic" alt="" @load="$refs.historyWaterfall.refresh()" @click="detailEvent(props.data)">
|
||||||
<div class="operate">
|
<div class="operate">
|
||||||
<div class="operate-wrap">
|
<div class="operate-wrap">
|
||||||
@@ -83,10 +136,11 @@
|
|||||||
<div class="name" @click="detailEvent(props.data)">{{props.data.name}}</div>
|
<div class="name" @click="detailEvent(props.data)">{{props.data.name}}</div>
|
||||||
<div class="info">
|
<div class="info">
|
||||||
<span v-if="props.data.time && props.data.duration">
|
<span v-if="props.data.time && props.data.duration">
|
||||||
{{fmtMSS(props.data.time.toFixed(0))}}/{{fmtMSS(props.data.duration.toFixed(0))}}
|
{{fmtMSS(props.data.time.toFixed(0))}}/{{fmtMSS(props.data.duration.toFixed(0))}} ({{progress(props.data)}}%)
|
||||||
</span>
|
</span>
|
||||||
<span v-if="props.data.detail && props.data.detail.m3u8List !== undefined && props.data.detail.m3u8List.length > 1">
|
<span v-if="props.data.onlinePlay">在线解析</span>
|
||||||
第{{ props.data.index + 1 }}集(共{{props.data.detail.m3u8List.length}}集)
|
<span v-if="props.data.detail && props.data.detail.fullList[0].list !== undefined && props.data.detail.fullList[0].list.length > 1">
|
||||||
|
第{{ props.data.index + 1 }}集(共{{props.data.detail.fullList[0].list.length}}集)
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -100,19 +154,34 @@
|
|||||||
import { mapMutations } from 'vuex'
|
import { mapMutations } from 'vuex'
|
||||||
import { history, sites, setting } from '../lib/dexie'
|
import { history, sites, setting } from '../lib/dexie'
|
||||||
import zy from '../lib/site/tools'
|
import zy from '../lib/site/tools'
|
||||||
import Sortable from 'sortablejs'
|
|
||||||
import { remote } from 'electron'
|
|
||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
import Waterfall from 'vue-waterfall-plugin'
|
import Waterfall from 'vue-waterfall-plugin'
|
||||||
|
const remote = require('@electron/remote')
|
||||||
const { clipboard } = require('electron')
|
const { clipboard } = require('electron')
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'history',
|
name: 'history',
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
history: [],
|
list: [],
|
||||||
sites: [],
|
sites: [],
|
||||||
viewMode: setting.historyViewMode
|
shiftDown: false,
|
||||||
|
selectionBegin: '',
|
||||||
|
selectionEnd: '',
|
||||||
|
multipleSelection: [],
|
||||||
|
areas: [],
|
||||||
|
types: [],
|
||||||
|
filteredList: [],
|
||||||
|
// Update
|
||||||
|
numNoUpdate: 0,
|
||||||
|
// Toolbar
|
||||||
|
showToolbar: false,
|
||||||
|
selectedAreas: [],
|
||||||
|
selectedTypes: [],
|
||||||
|
sortKeyword: '',
|
||||||
|
sortKeywords: ['按片名', '按上映年份', '按更新时间', '按完成度'],
|
||||||
|
selectedYears: { start: 0, end: new Date().getFullYear() },
|
||||||
|
onlyShowItemsHasUpdate: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
@@ -150,22 +219,181 @@ export default {
|
|||||||
set (val) {
|
set (val) {
|
||||||
this.SET_SHARE(val)
|
this.SET_SHARE(val)
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
setting: {
|
||||||
|
get () {
|
||||||
|
return this.$store.getters.getSetting
|
||||||
|
},
|
||||||
|
set (val) {
|
||||||
|
this.SET_SETTING(val)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
DetailCache: {
|
||||||
|
get () {
|
||||||
|
return this.$store.getters.getDetailCache
|
||||||
|
},
|
||||||
|
set (val) {
|
||||||
|
this.SET_DetailCache(val)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
view () {
|
view () {
|
||||||
this.getAllhistory()
|
if (this.view === 'History') {
|
||||||
this.getAllsites()
|
this.getAllhistory()
|
||||||
if (this.$refs.historyWaterfall) {
|
this.getAllsites()
|
||||||
this.$refs.historyWaterfall.refresh()
|
if (this.setting.historyViewMode === 'table') this.showShiftPrompt()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
list: {
|
||||||
|
handler (list) {
|
||||||
|
this.areas = [...new Set(list.map(ele => ele.detail.area))].filter(x => x)
|
||||||
|
this.types = [...new Set(list.map(ele => ele.detail.type))].filter(x => x)
|
||||||
|
this.refreshFilteredList()
|
||||||
|
},
|
||||||
|
deep: true
|
||||||
|
},
|
||||||
|
numNoUpdate () {
|
||||||
|
// 如果所有历史都没有更新的话
|
||||||
|
if (this.numNoUpdate === this.list.length) {
|
||||||
|
this.numNoUpdate = 0
|
||||||
|
this.$message.warning('未查询到任何更新')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapMutations(['SET_VIEW', 'SET_DETAIL', 'SET_VIDEO', 'SET_SHARE']),
|
...mapMutations(['SET_VIEW', 'SET_DETAIL', 'SET_VIDEO', 'SET_SHARE', 'SET_SETTING']),
|
||||||
|
updateAllEvent () {
|
||||||
|
this.numNoUpdate = 0
|
||||||
|
this.list.forEach(e => {
|
||||||
|
this.updateEvent(e)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
async updateEvent (e) {
|
||||||
|
try {
|
||||||
|
if (!this.DetailCache[e.site + '@' + e.ids]) {
|
||||||
|
this.DetailCache[e.site + '@' + e.ids] = await zy.detail(e.site, e.ids)
|
||||||
|
}
|
||||||
|
const newDetail = this.DetailCache[e.site + '@' + e.ids]
|
||||||
|
history.get(e.id).then(res => {
|
||||||
|
if (!e.hasUpdate && e.detail.last !== newDetail.last) {
|
||||||
|
res.hasUpdate = true
|
||||||
|
res.detail = newDetail
|
||||||
|
const msg = `检查到"${e.name}"有更新。`
|
||||||
|
this.$message.success(msg)
|
||||||
|
} else {
|
||||||
|
this.numNoUpdate += 1
|
||||||
|
}
|
||||||
|
history.update(e.id, res)
|
||||||
|
this.getAllhistory()
|
||||||
|
})
|
||||||
|
} catch (err) {
|
||||||
|
const msg = `更新"${e.name}"失败, 请重试。`
|
||||||
|
this.$message.warning(msg, err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
toggleViewMode () {
|
||||||
|
this.setting.historyViewMode = this.setting.historyViewMode === 'picture' ? 'table' : 'picture'
|
||||||
|
if (this.setting.historyViewMode === 'table') {
|
||||||
|
this.showShiftPrompt()
|
||||||
|
} else {
|
||||||
|
setTimeout(() => { if (this.$refs.historyWaterfall) this.$refs.historyWaterfall.refresh() }, 700)
|
||||||
|
}
|
||||||
|
setting.find().then(res => {
|
||||||
|
res.historyViewMode = this.setting.historyViewMode
|
||||||
|
setting.update(res)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
backTop () {
|
||||||
|
if (this.setting.starViewMode === 'picture') {
|
||||||
|
document.getElementById('history-body').scrollTop = 0
|
||||||
|
} else {
|
||||||
|
this.$refs.historyTable.bodyWrapper.scrollTop = 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
refreshFilteredList () {
|
||||||
|
if (!this.showToolbar) {
|
||||||
|
this.sortKeyword = ''
|
||||||
|
this.selectedAreas = []
|
||||||
|
this.selectedSearchClassNames = []
|
||||||
|
this.selectedYears.start = 0
|
||||||
|
this.selectedYears.end = new Date().getFullYear()
|
||||||
|
this.filteredList = this.list
|
||||||
|
} else {
|
||||||
|
let filteredData = this.list
|
||||||
|
filteredData = filteredData.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))
|
||||||
|
filteredData = filteredData.filter(res => res.detail.year >= this.selectedYears.start)
|
||||||
|
filteredData = filteredData.filter(res => res.detail.year <= this.selectedYears.end)
|
||||||
|
switch (this.sortKeyword) {
|
||||||
|
case '按上映年份':
|
||||||
|
filteredData.sort(function (a, b) {
|
||||||
|
return a.detail.year - b.detail.year
|
||||||
|
})
|
||||||
|
break
|
||||||
|
case '按片名':
|
||||||
|
filteredData.sort(function (a, b) {
|
||||||
|
return a.detail.name.localeCompare(b.detail.name, 'zh')
|
||||||
|
})
|
||||||
|
break
|
||||||
|
case '按更新时间':
|
||||||
|
filteredData.sort(function (a, b) {
|
||||||
|
return new Date(b.detail.last) - new Date(a.detail.last)
|
||||||
|
})
|
||||||
|
break
|
||||||
|
case '按完成度':
|
||||||
|
filteredData.sort(this.sortByProgress)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
this.filteredList = filteredData
|
||||||
|
}
|
||||||
|
if (this.onlyShowItemsHasUpdate) {
|
||||||
|
this.filteredList = this.filteredList.filter(x => x.hasUpdate)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
progress (e) {
|
||||||
|
return e.duration > 0 ? ((e.time / e.duration) * 100).toFixed(0) : 0
|
||||||
|
},
|
||||||
|
sortByProgress (a, b) {
|
||||||
|
if (this.progress(a) < this.progress(b)) {
|
||||||
|
return -1
|
||||||
|
} else {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
},
|
||||||
fmtMSS (s) {
|
fmtMSS (s) {
|
||||||
return (s - (s %= 60)) / 60 + (s > 9 ? ':' : ':0') + s
|
return (s - (s %= 60)) / 60 + (s > 9 ? ':' : ':0') + s
|
||||||
},
|
},
|
||||||
|
selectionCellClick (selection, row) { // 历史id与顺序刚好相反,大的反而在前面
|
||||||
|
if (this.shiftDown && this.selectionBegin !== '' && selection.includes(row)) {
|
||||||
|
this.selectionEnd = row.id
|
||||||
|
const start = this.list.findIndex(e => e.id === Math.max(this.selectionBegin, this.selectionEnd))
|
||||||
|
const end = this.list.findIndex(e => e.id === Math.min(this.selectionBegin, this.selectionEnd))
|
||||||
|
const selections = this.list.slice(start, end + 1)
|
||||||
|
this.$nextTick(() => {
|
||||||
|
selections.forEach(e => this.$refs.historyTable.toggleRowSelection(e, true))
|
||||||
|
})
|
||||||
|
this.selectionBegin = this.selectionEnd = ''
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (selection.includes(row)) {
|
||||||
|
this.selectionBegin = row.id
|
||||||
|
} else {
|
||||||
|
this.selectionBegin = ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleSelectionChange (rows) {
|
||||||
|
this.multipleSelection = rows
|
||||||
|
},
|
||||||
|
removeSelectedItems () {
|
||||||
|
if (!this.multipleSelection.length) this.multipleSelection = this.list
|
||||||
|
this.multipleSelection.forEach(e => history.remove(e.id))
|
||||||
|
this.multipleSelection = []
|
||||||
|
this.getAllhistory()
|
||||||
|
this.updateDatabase()
|
||||||
|
},
|
||||||
detailEvent (e) {
|
detailEvent (e) {
|
||||||
this.detail = {
|
this.detail = {
|
||||||
show: true,
|
show: true,
|
||||||
@@ -175,6 +403,9 @@ export default {
|
|||||||
name: e.name
|
name: e.name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (e.hasUpdate) {
|
||||||
|
this.clearHasUpdateFlag(e)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
async playEvent (e) {
|
async playEvent (e) {
|
||||||
const db = await history.find({ site: e.site, ids: e.ids })
|
const db = await history.find({ site: e.site, ids: e.ids })
|
||||||
@@ -183,46 +414,37 @@ export default {
|
|||||||
} else {
|
} else {
|
||||||
this.video = { key: e.site, info: { id: e.ids, name: e.name, index: 0 } }
|
this.video = { key: e.site, info: { id: e.ids, name: e.name, index: 0 } }
|
||||||
}
|
}
|
||||||
zy.detail(e.site, e.ids).then(detailRes => {
|
if (e.hasUpdate) {
|
||||||
this.video.detail = detailRes
|
this.clearHasUpdateFlag(e)
|
||||||
})
|
}
|
||||||
this.view = 'Play'
|
this.view = 'Play'
|
||||||
},
|
},
|
||||||
|
async clearHasUpdateFlag (e) {
|
||||||
|
const db = await history.find({ id: e.id })
|
||||||
|
if (db) {
|
||||||
|
db.hasUpdate = false
|
||||||
|
history.update(e.id, db)
|
||||||
|
this.getAllhistory()
|
||||||
|
}
|
||||||
|
},
|
||||||
shareEvent (e) {
|
shareEvent (e) {
|
||||||
this.share = {
|
this.share = {
|
||||||
show: true,
|
show: true,
|
||||||
key: e.site,
|
key: e.site,
|
||||||
info: e
|
info: e.detail
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
downloadEvent (e) {
|
downloadEvent (e) {
|
||||||
zy.download(e.site, e.ids).then(res => {
|
zy.download(e.site, e.ids, e.videoFlag).then(res => {
|
||||||
if (res && res.m3u8List) {
|
clipboard.writeText(res.downloadUrls)
|
||||||
const list = res.m3u8List.split('#')
|
this.$message.success(res.info)
|
||||||
let downloadUrl = ''
|
}).catch((err) => {
|
||||||
for (const i of list) {
|
this.$message.error(err.info)
|
||||||
const url = encodeURI(i.split('$')[1])
|
|
||||||
downloadUrl += (url + '\n')
|
|
||||||
}
|
|
||||||
clipboard.writeText(downloadUrl)
|
|
||||||
this.$message.success('『MP4』格式的链接已复制, 快去下载吧!')
|
|
||||||
} else {
|
|
||||||
zy.detail(e.site, e.ids).then(res => {
|
|
||||||
const list = [...res.m3u8List]
|
|
||||||
let downloadUrl = ''
|
|
||||||
for (const i of list) {
|
|
||||||
const url = encodeURI(i.split('$')[1])
|
|
||||||
downloadUrl += (url + '\n')
|
|
||||||
}
|
|
||||||
clipboard.writeText(downloadUrl)
|
|
||||||
this.$message.success('『M3U8』格式的链接已复制, 快去下载吧!')
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
exportHistory () {
|
exportHistory () {
|
||||||
this.getAllhistory()
|
this.getAllhistory()
|
||||||
const arr = [...this.history]
|
const arr = [...this.list]
|
||||||
const str = JSON.stringify(arr, null, 2)
|
const str = JSON.stringify(arr, null, 2)
|
||||||
const options = {
|
const options = {
|
||||||
filters: [
|
filters: [
|
||||||
@@ -231,6 +453,7 @@ export default {
|
|||||||
}
|
}
|
||||||
remote.dialog.showSaveDialog(options).then(result => {
|
remote.dialog.showSaveDialog(options).then(result => {
|
||||||
if (!result.canceled) {
|
if (!result.canceled) {
|
||||||
|
if (!result.filePath.endsWith('.json')) result.filePath += '.json'
|
||||||
fs.writeFileSync(result.filePath, str)
|
fs.writeFileSync(result.filePath, str)
|
||||||
this.$message.success('已保存成功')
|
this.$message.success('已保存成功')
|
||||||
}
|
}
|
||||||
@@ -248,8 +471,14 @@ export default {
|
|||||||
remote.dialog.showOpenDialog(options).then(result => {
|
remote.dialog.showOpenDialog(options).then(result => {
|
||||||
if (!result.canceled) {
|
if (!result.canceled) {
|
||||||
result.filePaths.forEach(file => {
|
result.filePaths.forEach(file => {
|
||||||
var str = fs.readFileSync(file)
|
const str = fs.readFileSync(file)
|
||||||
const json = JSON.parse(str)
|
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 => {
|
history.bulkAdd(json).then(res => {
|
||||||
this.$message.success('导入成功')
|
this.$message.success('导入成功')
|
||||||
this.getAllhistory()
|
this.getAllhistory()
|
||||||
@@ -258,14 +487,9 @@ export default {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
clearAllHistory () {
|
|
||||||
history.clear().then(res => {
|
|
||||||
this.history = []
|
|
||||||
})
|
|
||||||
},
|
|
||||||
getAllhistory () {
|
getAllhistory () {
|
||||||
history.all().then(res => {
|
history.all().then(res => {
|
||||||
this.history = res.reverse()
|
this.list = res.reverse()
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
getAllsites () {
|
getAllsites () {
|
||||||
@@ -274,7 +498,7 @@ export default {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
getSiteName (key) {
|
getSiteName (key) {
|
||||||
var site = this.sites.find(e => e.key === key)
|
const site = this.sites.find(e => e.key === key)
|
||||||
if (site) {
|
if (site) {
|
||||||
return site.name
|
return site.name
|
||||||
}
|
}
|
||||||
@@ -286,54 +510,37 @@ export default {
|
|||||||
this.$message.warning('删除历史记录失败, 错误信息: ' + err)
|
this.$message.warning('删除历史记录失败, 错误信息: ' + err)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
updateDatabase (data) {
|
updateDatabase () {
|
||||||
history.clear().then(res => {
|
history.clear().then(res => {
|
||||||
var id = length
|
let id = length
|
||||||
data.forEach(ele => {
|
this.list.forEach(ele => {
|
||||||
ele.id = id
|
ele.id = id
|
||||||
id -= 1
|
id -= 1
|
||||||
history.add(ele)
|
history.add(ele)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
rowDrop () {
|
showShiftPrompt () {
|
||||||
const tbody = document.getElementById('history-table').querySelector('.el-table__body-wrapper tbody')
|
if (this.setting.shiftTooltipLimitTimes === undefined) this.setting.shiftTooltipLimitTimes = 5
|
||||||
const _this = this
|
if (this.setting.shiftTooltipLimitTimes) {
|
||||||
Sortable.create(tbody, {
|
this.$message.info('多选时支持shift快捷键')
|
||||||
onEnd ({ newIndex, oldIndex }) {
|
this.setting.shiftTooltipLimitTimes--
|
||||||
const currRow = _this.history.splice(oldIndex, 1)[0]
|
setting.find().then(res => {
|
||||||
_this.history.splice(newIndex, 0, currRow)
|
res.shiftTooltipLimitTimes = this.setting.shiftTooltipLimitTimes
|
||||||
_this.updateDatabase(_this.history)
|
setting.update(res)
|
||||||
}
|
})
|
||||||
})
|
}
|
||||||
},
|
|
||||||
getViewMode () {
|
|
||||||
setting.find().then(res => {
|
|
||||||
this.viewMode = res.historyViewMode
|
|
||||||
})
|
|
||||||
},
|
|
||||||
updateViewMode () {
|
|
||||||
setting.find().then(res => {
|
|
||||||
res.historyViewMode = this.viewMode
|
|
||||||
setting.update(res)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted () {
|
mounted () {
|
||||||
this.rowDrop()
|
addEventListener('keydown', code => { if (code.keyCode === 16) this.shiftDown = true })
|
||||||
window.addEventListener('resize', () => {
|
addEventListener('keyup', code => { if (code.keyCode === 16) this.shiftDown = false })
|
||||||
if (this.$refs.historyWaterfall && this.view === 'History') {
|
addEventListener('resize', () => {
|
||||||
this.$refs.historyWaterfall.resize()
|
setTimeout(() => { if (this.$refs.historyWaterfall) this.$refs.historyWaterfall.resize() }, 500)
|
||||||
this.$refs.historyWaterfall.refresh()
|
})
|
||||||
setTimeout(() => {
|
|
||||||
this.$refs.historyWaterfall.refresh()
|
|
||||||
}, 500)
|
|
||||||
}
|
|
||||||
}, false)
|
|
||||||
},
|
},
|
||||||
created () {
|
created () {
|
||||||
this.getAllhistory()
|
this.getAllhistory()
|
||||||
this.getViewMode()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -2,17 +2,17 @@
|
|||||||
<div class="listpage" id="iptv">
|
<div class="listpage" id="iptv">
|
||||||
<div class="listpage-header" id="iptv-header" v-show="!enableBatchEdit">
|
<div class="listpage-header" id="iptv-header" v-show="!enableBatchEdit">
|
||||||
<el-switch v-model="enableBatchEdit" active-text="批处理及频道调整"></el-switch>
|
<el-switch v-model="enableBatchEdit" active-text="批处理及频道调整"></el-switch>
|
||||||
<el-button @click.stop="exportChannels" icon="el-icon-upload2" >导出</el-button>
|
<el-button @click.stop="exportChannels" icon="el-icon-upload2" title="导出m3u时必须手动添加扩展名,要保存频道配置信息请选择json格式">导出</el-button>
|
||||||
<el-button @click.stop="importChannels" icon="el-icon-download">导入</el-button>
|
<el-button @click.stop="importChannels" icon="el-icon-download" title='支持同时导入多个文件,导入m3u时网址可带参数、含有"#"号时自动分割'>导入</el-button>
|
||||||
<el-button @click="checkAllChannels" icon="el-icon-refresh" :loading="checkAllChannelsLoading">检测{{ this.checkAllChannelsLoading ? this.checkProgress + '/' + this.iptvList.length : '' }}</el-button>
|
<el-button @click="checkAllChannels" icon="el-icon-refresh" :loading="checkAllChannelsLoading" title="可在后台运行">检测{{ this.checkAllChannelsLoading ? this.checkProgress + '/' + this.iptvList.length : '' }}</el-button>
|
||||||
<el-button @click.stop="resetChannelsEvent" icon="el-icon-refresh-left">重置</el-button>
|
<el-button @click.stop="resetChannelsEvent" icon="el-icon-refresh-left">重置</el-button>
|
||||||
</div>
|
</div>
|
||||||
<div class="listpage-header" id="iptv-header" v-show="enableBatchEdit">
|
<div class="listpage-header" id="iptv-header" v-show="enableBatchEdit">
|
||||||
<el-switch v-model="enableBatchEdit" active-text="批处理及频道调整"></el-switch>
|
<el-switch v-model="enableBatchEdit" active-text="批处理及频道调整"></el-switch>
|
||||||
<el-input placeholder="新组名/新频道名" v-model="inputContent"></el-input>
|
<el-input placeholder="新组名/新频道名" v-model="inputContent"></el-input>
|
||||||
<el-switch v-model="batchIsActive" active-text="启用"></el-switch>
|
<el-switch v-model="batchIsActive" active-text="启用"></el-switch>
|
||||||
<el-button type="primary" icon="el-icon-edit" @click.stop="saveBatchEdit">保存分组与开关状态</el-button>
|
<el-button type="primary" icon="el-icon-edit" @click.stop="saveBatchEdit" title="输入框组名为空时仅保存开关状态">保存分组与开关状态</el-button>
|
||||||
<el-button type="primary" icon="el-icon-film" @click.stop="mergeChannel">{{ this.multipleSelection.length === 1 ? '频道重命名' : '频道合并' }}</el-button>
|
<el-button type="primary" icon="el-icon-film" @click.stop="mergeChannel" title="勾选单个时可重命名频道">{{ this.multipleSelection.length === 1 ? '频道重命名' : '频道合并' }}</el-button>
|
||||||
<el-button @click.stop="removeSelectedChannels" icon="el-icon-delete-solid">删除</el-button>
|
<el-button @click.stop="removeSelectedChannels" icon="el-icon-delete-solid">删除</el-button>
|
||||||
</div>
|
</div>
|
||||||
<div class="listpage-body" id="iptv-table">
|
<div class="listpage-body" id="iptv-table">
|
||||||
@@ -107,12 +107,13 @@
|
|||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import { mapMutations } from 'vuex'
|
import { mapMutations } from 'vuex'
|
||||||
import { iptv, channelList } from '../lib/dexie'
|
import { iptv, channelList, setting } from '../lib/dexie'
|
||||||
import { iptv as defaultChannels } from '../lib/dexie/initData'
|
import { iptv as defaultChannels } from '../lib/dexie/initData'
|
||||||
import zy from '../lib/site/tools'
|
import zy from '../lib/site/tools'
|
||||||
import { remote } from 'electron'
|
|
||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
import Sortable from 'sortablejs'
|
import Sortable from 'sortablejs'
|
||||||
|
import axios from 'axios'
|
||||||
|
const remote = require('@electron/remote')
|
||||||
export default {
|
export default {
|
||||||
name: 'iptv',
|
name: 'iptv',
|
||||||
data () {
|
data () {
|
||||||
@@ -143,8 +144,13 @@ export default {
|
|||||||
this.SET_VIEW(val)
|
this.SET_VIEW(val)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setting () {
|
setting: {
|
||||||
return this.$store.getters.getSetting
|
get () {
|
||||||
|
return this.$store.getters.getSetting
|
||||||
|
},
|
||||||
|
set (val) {
|
||||||
|
this.SET_SETTING(val)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
video: {
|
video: {
|
||||||
get () {
|
get () {
|
||||||
@@ -163,9 +169,9 @@ export default {
|
|||||||
},
|
},
|
||||||
getFilters () {
|
getFilters () {
|
||||||
const groups = [...new Set(this.channelList.map(iptv => iptv.group))]
|
const groups = [...new Set(this.channelList.map(iptv => iptv.group))]
|
||||||
var filters = []
|
const filters = []
|
||||||
groups.forEach(g => {
|
groups.forEach(g => {
|
||||||
var doc = {
|
const doc = {
|
||||||
text: g,
|
text: g,
|
||||||
value: g
|
value: g
|
||||||
}
|
}
|
||||||
@@ -175,11 +181,6 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
view () {
|
|
||||||
if (this.view === 'IPTV' && !this.checkAllChannelsLoading) {
|
|
||||||
this.getChannelList()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
enableBatchEdit () {
|
enableBatchEdit () {
|
||||||
if (this.checkAllChannelsLoading) {
|
if (this.checkAllChannelsLoading) {
|
||||||
this.$message.info('正在检测, 请勿操作.')
|
this.$message.info('正在检测, 请勿操作.')
|
||||||
@@ -187,6 +188,15 @@ export default {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (this.enableBatchEdit) {
|
if (this.enableBatchEdit) {
|
||||||
|
if (this.setting.shiftTooltipLimitTimes === undefined) this.setting.shiftTooltipLimitTimes = 5
|
||||||
|
if (this.setting.shiftTooltipLimitTimes) {
|
||||||
|
this.$message.info('多选时支持shift快捷键')
|
||||||
|
this.setting.shiftTooltipLimitTimes--
|
||||||
|
setting.find().then(res => {
|
||||||
|
res.shiftTooltipLimitTimes = this.setting.shiftTooltipLimitTimes
|
||||||
|
setting.update(res)
|
||||||
|
})
|
||||||
|
}
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
this.expandedRows.forEach(e => this.$refs.iptvTable.toggleRowExpansion(e, false))
|
this.expandedRows.forEach(e => this.$refs.iptvTable.toggleRowExpansion(e, false))
|
||||||
})
|
})
|
||||||
@@ -197,7 +207,7 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapMutations(['SET_VIEW', 'SET_DETAIL', 'SET_VIDEO', 'SET_SHARE']),
|
...mapMutations(['SET_VIEW', 'SET_DETAIL', 'SET_VIDEO', 'SET_SHARE', 'SET_SETTING']),
|
||||||
sortByLocaleCompare (a, b) {
|
sortByLocaleCompare (a, b) {
|
||||||
return a.localeCompare(b, 'zh')
|
return a.localeCompare(b, 'zh')
|
||||||
},
|
},
|
||||||
@@ -248,7 +258,7 @@ export default {
|
|||||||
},
|
},
|
||||||
mergeChannel () {
|
mergeChannel () {
|
||||||
if (this.inputContent && this.multipleSelection.length) {
|
if (this.inputContent && this.multipleSelection.length) {
|
||||||
var channels = []
|
let channels = []
|
||||||
const id = this.multipleSelection[0].id
|
const id = this.multipleSelection[0].id
|
||||||
this.multipleSelection.forEach(ele => {
|
this.multipleSelection.forEach(ele => {
|
||||||
channels = channels.concat(ele.channels)
|
channels = channels.concat(ele.channels)
|
||||||
@@ -265,8 +275,13 @@ export default {
|
|||||||
if (e.url) {
|
if (e.url) {
|
||||||
this.video = { iptv: e }
|
this.video = { iptv: e }
|
||||||
} else {
|
} else {
|
||||||
const prefer = e.prefer ? e.channels.find(c => c.id === e.prefer) : e.channels.filter(c => c.isActive)[0]
|
let prefer
|
||||||
if (!prefer) return
|
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.video = { iptv: prefer }
|
||||||
}
|
}
|
||||||
this.view = 'Play'
|
this.view = 'Play'
|
||||||
@@ -289,6 +304,7 @@ export default {
|
|||||||
ele.channels.splice(ele.channels.findIndex(e => e.id === row.id), 1)
|
ele.channels.splice(ele.channels.findIndex(e => e.id === row.id), 1)
|
||||||
channelList.remove(row.channelID)
|
channelList.remove(row.channelID)
|
||||||
if (ele.channels.length) {
|
if (ele.channels.length) {
|
||||||
|
if (ele.prefer === row.id) delete ele.prefer
|
||||||
if (ele.channels.length === 1) ele.hasChildren = false
|
if (ele.channels.length === 1) ele.hasChildren = false
|
||||||
channelList.add(ele)
|
channelList.add(ele)
|
||||||
this.$set(this.$refs.iptvTable.store.states.lazyTreeNodeMap, ele.id, ele.channels)
|
this.$set(this.$refs.iptvTable.store.states.lazyTreeNodeMap, ele.id, ele.channels)
|
||||||
@@ -311,13 +327,14 @@ export default {
|
|||||||
remote.dialog.showSaveDialog(options).then(result => {
|
remote.dialog.showSaveDialog(options).then(result => {
|
||||||
if (!result.canceled) {
|
if (!result.canceled) {
|
||||||
if (result.filePath.endsWith('m3u')) {
|
if (result.filePath.endsWith('m3u')) {
|
||||||
var writer = require('m3u').extendedWriter()
|
const writer = require('m3u').extendedWriter()
|
||||||
this.iptvList.forEach(e => {
|
this.iptvList.forEach(e => {
|
||||||
writer.file(e.url, -1, e.name)
|
writer.file(e.url, -1, e.name)
|
||||||
})
|
})
|
||||||
fs.writeFileSync(result.filePath, writer.toString())
|
fs.writeFileSync(result.filePath, writer.toString())
|
||||||
this.$message.success('已保存成功')
|
this.$message.success('已保存成功')
|
||||||
} else {
|
} else {
|
||||||
|
if (!result.filePath.endsWith('.json')) result.filePath += '.json'
|
||||||
const arr = [...this.channelList] // 要保存channelList必须选json
|
const arr = [...this.channelList] // 要保存channelList必须选json
|
||||||
const str = JSON.stringify(arr, null, 2)
|
const str = JSON.stringify(arr, null, 2)
|
||||||
fs.writeFileSync(result.filePath, str)
|
fs.writeFileSync(result.filePath, str)
|
||||||
@@ -333,10 +350,55 @@ export default {
|
|||||||
this.$message.info('正在检测, 请勿操作.')
|
this.$message.info('正在检测, 请勿操作.')
|
||||||
return false
|
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 = {
|
const options = {
|
||||||
filters: [
|
filters: [
|
||||||
{ name: 'm3u file', extensions: ['m3u', 'm3u8'] },
|
{ name: '支持的文件格式', extensions: ['m3u', 'm3u8', 'json', 'txt'] }
|
||||||
{ name: 'JSON file', extensions: ['json'] }
|
|
||||||
],
|
],
|
||||||
properties: ['openFile', 'multiSelections']
|
properties: ['openFile', 'multiSelections']
|
||||||
}
|
}
|
||||||
@@ -350,11 +412,12 @@ export default {
|
|||||||
const parser = require('iptv-playlist-parser')
|
const parser = require('iptv-playlist-parser')
|
||||||
const playlist = fs.readFileSync(file, { encoding: 'utf-8' })
|
const playlist = fs.readFileSync(file, { encoding: 'utf-8' })
|
||||||
const result = parser.parse(playlist)
|
const result = parser.parse(playlist)
|
||||||
|
const supportFormats = /\.(m3u8|flv)$/
|
||||||
result.items.forEach(ele => {
|
result.items.forEach(ele => {
|
||||||
const urls = ele.url.split('#').filter(e => e.startsWith('http')) // 网址带#时自动分割
|
const urls = ele.url.split('#').filter(e => e.startsWith('http')) // 网址带#时自动分割
|
||||||
urls.forEach(url => {
|
urls.forEach(url => {
|
||||||
if (ele.name && url && new URL.URL(url).pathname.endsWith('.m3u8')) { // 网址可能带参数
|
if (ele.name && url && (supportFormats.test(url) || supportFormats.test(new URL.URL(url).pathname))) { // 网址可能带参数
|
||||||
var doc = {
|
const doc = {
|
||||||
id: id,
|
id: id,
|
||||||
name: ele.name,
|
name: ele.name,
|
||||||
url: url,
|
url: url,
|
||||||
@@ -372,8 +435,9 @@ export default {
|
|||||||
this.updateChannelList()
|
this.updateChannelList()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
} else {
|
this.$message.success('导入成功')
|
||||||
// Import Json file
|
}
|
||||||
|
if (file.endsWith('json')) {
|
||||||
const importedList = JSON.parse(fs.readFileSync(file))
|
const importedList = JSON.parse(fs.readFileSync(file))
|
||||||
importedList.forEach(ele => {
|
importedList.forEach(ele => {
|
||||||
const commonEle = this.channelList.find(e => e.name === ele.name)
|
const commonEle = this.channelList.find(e => e.name === ele.name)
|
||||||
@@ -387,21 +451,51 @@ export default {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
this.updateDatabase()
|
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('导入成功')
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
determineGroup (name) {
|
determineGroup (name) {
|
||||||
if (name.toLowerCase().includes('cctv') && (name.includes('蓝光') || name.includes('高清'))) {
|
if (name.toLowerCase().includes('cctv') || name.toLowerCase().includes('cgtn')) {
|
||||||
return '央视高清'
|
|
||||||
} else if (name.toLowerCase().includes('cctv')) {
|
|
||||||
return '央视'
|
return '央视'
|
||||||
|
} else if (name.includes('香港') || name.includes('澳门') || name.includes('台湾') || name.includes('凤凰') || name.includes('翡翠')) {
|
||||||
|
return '港澳台'
|
||||||
} else if (name.includes('卫视')) {
|
} else if (name.includes('卫视')) {
|
||||||
return '卫视'
|
return '卫视'
|
||||||
} else if (name.includes('香港') || name.includes('澳门') || name.includes('台湾') || name.includes('凤凰')) {
|
|
||||||
return '港澳台'
|
|
||||||
} else if (name.includes('高清') || name.includes('蓝光') || name.includes('1080P')) {
|
} else if (name.includes('高清') || name.includes('蓝光') || name.includes('1080P')) {
|
||||||
return '高清'
|
return '高清'
|
||||||
} else {
|
} else {
|
||||||
@@ -430,12 +524,12 @@ export default {
|
|||||||
res = res.filter(o => !this.iptvList.find(e => o.url === e.url))
|
res = res.filter(o => !this.iptvList.find(e => o.url === e.url))
|
||||||
const resClone = JSON.parse(JSON.stringify(res))
|
const resClone = JSON.parse(JSON.stringify(res))
|
||||||
const uniqueChannelName = {}
|
const uniqueChannelName = {}
|
||||||
for (var i = 0; i < resClone.length; i++) {
|
for (let i = 0; i < resClone.length; i++) {
|
||||||
var channelName = resClone[i].name.trim().replace(/[- ]?(1080p|蓝光|超清|高清|标清|hd|cq|4k)(\d{1,2})?$/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 (channelName.match(/cctv/i)) channelName = channelName.replace('-', '')
|
||||||
if (Object.keys(uniqueChannelName).some(name => channelName.match(new RegExp(`${name}(1080p|4k|(?!\\d))`, 'i')))) continue // 避免重复
|
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')
|
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)) {
|
if (resClone[j].name.match(/cctv/i)) {
|
||||||
resClone[j].name = resClone[j].name.replace('-', '')
|
resClone[j].name = resClone[j].name.replace('-', '')
|
||||||
}
|
}
|
||||||
@@ -505,7 +599,7 @@ export default {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
resetId (channelList) {
|
resetId (channelList) {
|
||||||
var id = 1
|
let id = 1
|
||||||
channelList.forEach(ele => {
|
channelList.forEach(ele => {
|
||||||
ele.id = id
|
ele.id = id
|
||||||
id += 1
|
id += 1
|
||||||
@@ -552,6 +646,7 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
async checkAllChannels () {
|
async checkAllChannels () {
|
||||||
|
if (this.checkAllChannelsLoading) return
|
||||||
this.checkAllChannelsLoading = true
|
this.checkAllChannelsLoading = true
|
||||||
this.stopFlag = false
|
this.stopFlag = false
|
||||||
this.checkProgress = 0
|
this.checkProgress = 0
|
||||||
@@ -562,10 +657,11 @@ export default {
|
|||||||
await this.checkChannelsBySite(other).then(res => {
|
await this.checkChannelsBySite(other).then(res => {
|
||||||
this.checkAllChannelsLoading = false
|
this.checkAllChannelsLoading = false
|
||||||
this.getChannelList()
|
this.getChannelList()
|
||||||
|
if (!this.stopFlag) this.$message.success('直播频道批量检测已完成!')
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
async checkChannelsBySite (channels) {
|
async checkChannelsBySite (channels) {
|
||||||
var siteList = {}
|
const siteList = {}
|
||||||
channels.forEach(channel => {
|
channels.forEach(channel => {
|
||||||
const site = channel.url.split('/')[2]
|
const site = channel.url.split('/')[2]
|
||||||
if (siteList[site]) {
|
if (siteList[site]) {
|
||||||
@@ -582,44 +678,50 @@ export default {
|
|||||||
await this.checkSingleChannel(c)
|
await this.checkSingleChannel(c)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async checkSingleChannel (channel) {
|
async checkSingleChannel (channel, force = false) {
|
||||||
if (this.setting.allowPassWhenIptvCheck && !channel.isActive) {
|
if (this.stopFlag) {
|
||||||
this.checkProgress += 1
|
this.checkProgress += 1
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
channel.status = ' '
|
|
||||||
const ele = this.channelList.find(e => e.id === channel.channelID)
|
const ele = this.channelList.find(e => e.id === channel.channelID)
|
||||||
if (this.stopFlag) {
|
if (!force && this.setting.allowPassWhenIptvCheck && (!channel.isActive || !ele.isActive)) {
|
||||||
this.checkProgress += 1
|
if (!ele.isActive) {
|
||||||
return channel.status
|
ele.status = '跳过'
|
||||||
}
|
} else if (!channel.isActive) {
|
||||||
const flag = await zy.checkChannel(channel.url)
|
channel.status = '跳过'
|
||||||
this.checkProgress += 1
|
}
|
||||||
ele.hasCheckedNum++
|
|
||||||
if (flag) {
|
|
||||||
channel.status = '可用'
|
|
||||||
} else {
|
} else {
|
||||||
channel.status = '失效'
|
channel.status = ' '
|
||||||
channel.isActive = false
|
const flag = await zy.checkChannel(channel.url)
|
||||||
if (this.setting.autocleanWhenIptvCheck) {
|
if (flag) {
|
||||||
ele.channels.splice(ele.channels.findIndex(e => e.id === channel.id), 1)
|
channel.status = '可用'
|
||||||
ele.hasCheckedNum--
|
} 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) {
|
if (ele.hasCheckedNum === ele.channels.length) {
|
||||||
ele.status = ele.channels.some(channel => channel.status === '可用') ? '可用' : '失效'
|
if (ele.status === ' ') {
|
||||||
if (ele.status === '失效') ele.isActive = false
|
ele.status = ele.channels.some(channel => channel.status === '可用') ? '可用' : '失效'
|
||||||
|
if (ele.status === '失效') ele.isActive = false
|
||||||
|
}
|
||||||
channelList.remove(channel.channelID)
|
channelList.remove(channel.channelID)
|
||||||
if (ele.channels.length === 1) ele.hasChildren = false
|
if (ele.channels.length === 1) ele.hasChildren = false
|
||||||
if (ele.channels.length) channelList.add(ele)
|
if (ele.channels.length) channelList.add(ele)
|
||||||
}
|
}
|
||||||
return channel.status
|
|
||||||
},
|
},
|
||||||
async checkChannel (row) {
|
async checkChannel (row) {
|
||||||
if (row.channels) {
|
if (row.channels) {
|
||||||
row.status = ' '
|
row.status = ' '
|
||||||
row.hasCheckedNum = 0
|
row.hasCheckedNum = 0
|
||||||
row.channels.forEach(e => this.checkSingleChannel(e))
|
row.channels.forEach(e => this.checkSingleChannel(e, true))
|
||||||
} else {
|
} else {
|
||||||
this.checkSingleChannel(row)
|
this.checkSingleChannel(row)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,39 +1,63 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="listpage" id="recommendataions">
|
<div class="listpage" id="recommendations">
|
||||||
<div class="listpage-header" id="recommendataions-header">
|
<div class="listpage-header" id="recommendations-header">
|
||||||
<el-switch v-model="viewMode" active-text="海报" active-value="picture" inactive-text="列表" inactive-value="table" @change="updateViewMode"></el-switch>
|
<el-select v-model="selectedRecommendationType" size="small" slot="prepend"
|
||||||
|
:popper-append-to-body="false"
|
||||||
|
popper-class="popper"
|
||||||
|
default-first-option placeholder="请选择"
|
||||||
|
@change="changeRecommendationTypeEvent">
|
||||||
|
<el-option
|
||||||
|
v-for="item in recommendationTypes"
|
||||||
|
:key="item"
|
||||||
|
:label="item"
|
||||||
|
:value="item">
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
<el-button type="text">视频数:{{ recommendations.length }}</el-button>
|
<el-button type="text">视频数:{{ recommendations.length }}</el-button>
|
||||||
<el-select v-model="selectedAreas" size="small" multiple collapse-tags placeholder="地区" popper-class="popper" :popper-append-to-body="false">
|
|
||||||
<el-option
|
|
||||||
v-for="item in areas"
|
|
||||||
:key="item"
|
|
||||||
:label="item"
|
|
||||||
:value="item">
|
|
||||||
</el-option>
|
|
||||||
</el-select>
|
|
||||||
<el-select v-model="selectedTypes" size="small" multiple collapse-tags placeholder="类型" popper-class="popper" :popper-append-to-body="false">
|
|
||||||
<el-option
|
|
||||||
v-for="item in types"
|
|
||||||
:key="item"
|
|
||||||
:label="item"
|
|
||||||
:value="item">
|
|
||||||
</el-option>
|
|
||||||
</el-select>
|
|
||||||
<el-select v-model="sortKeyword" size="small" placeholder="排序" popper-class="popper" :popper-append-to-body="false">
|
|
||||||
<el-option
|
|
||||||
v-for="item in sortKeywords"
|
|
||||||
:key="item"
|
|
||||||
:label="item"
|
|
||||||
:value="item">
|
|
||||||
</el-option>
|
|
||||||
</el-select>
|
|
||||||
<el-button :loading="loading" @click.stop="updateEvent" icon="el-icon-refresh">更新推荐</el-button>
|
<el-button :loading="loading" @click.stop="updateEvent" icon="el-icon-refresh">更新推荐</el-button>
|
||||||
</div>
|
</div>
|
||||||
<div class="listpage-body" id="recommendataions-body" >
|
<div class="toolbar" v-show="showToolbar">
|
||||||
<div class="show-table" id="star-table" v-show="viewMode === 'table'">
|
<el-select v-model="selectedAreas" size="small" multiple placeholder="地区" popper-class="popper" :popper-append-to-body="false" @remove-tag="refreshFilteredList" @change="refreshFilteredList">
|
||||||
|
<el-option
|
||||||
|
v-for="item in areas"
|
||||||
|
:key="item"
|
||||||
|
:label="item"
|
||||||
|
:value="item">
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
<el-select v-model="selectedTypes" size="small" multiple placeholder="类型" popper-class="popper" :popper-append-to-body="false" @remove-tag="refreshFilteredList" @change="refreshFilteredList">
|
||||||
|
<el-option
|
||||||
|
v-for="item in types"
|
||||||
|
:key="item"
|
||||||
|
:label="item"
|
||||||
|
:value="item">
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
<el-select v-model="sortKeyword" size="small" placeholder="排序" popper-class="popper" :popper-append-to-body="false" @change="refreshFilteredList">
|
||||||
|
<el-option
|
||||||
|
v-for="item in sortKeywords"
|
||||||
|
:key="item"
|
||||||
|
:label="item"
|
||||||
|
:value="item">
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
<span>
|
||||||
|
上映区间:
|
||||||
|
<el-input-number size="small" v-model="selectedYears.start" :min=0 :max="new Date().getFullYear()" controls-position="right" step-strictly @change="refreshFilteredList"></el-input-number>
|
||||||
|
至
|
||||||
|
<el-input-number size="small" v-model="selectedYears.end" :min=0 :max="new Date().getFullYear()" controls-position="right" step-strictly @change="refreshFilteredList"></el-input-number>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<el-divider class="listpage-header-divider" content-position="right">
|
||||||
|
<el-button type="text" size="mini" @click="toggleViewMode">视图切换</el-button>
|
||||||
|
<el-button type="text" size="mini" @click='() => { showToolbar = !showToolbar; if (!showToolbar) this.refreshFilteredList() }' title="收起工具栏会重置筛选排序">{{ showToolbar ? '隐藏工具栏' : '显示工具栏' }}</el-button>
|
||||||
|
<el-button type="text" size="mini" @click="backTop">回到顶部</el-button>
|
||||||
|
</el-divider>
|
||||||
|
<div class="listpage-body" id="recommendations-body" >
|
||||||
|
<div class="show-table" id="star-table" v-if="setting.recommendationViewMode === 'table'">
|
||||||
<el-table size="mini" fit height="100%" row-key="id"
|
<el-table size="mini" fit height="100%" row-key="id"
|
||||||
ref="recommendataionsTable"
|
ref="recommendationsTable"
|
||||||
:data="filteredRecommendations"
|
:data="filteredList"
|
||||||
@row-click="detailEvent">
|
@row-click="detailEvent">
|
||||||
<el-table-column
|
<el-table-column
|
||||||
prop="name"
|
prop="name"
|
||||||
@@ -55,13 +79,13 @@
|
|||||||
width="100"
|
width="100"
|
||||||
align="center">
|
align="center">
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column v-if="filteredRecommendations.some(e => e.rate)"
|
<el-table-column v-if="filteredList.some(e => e.rate)"
|
||||||
prop="rate"
|
prop="rate"
|
||||||
align="center"
|
align="center"
|
||||||
width="100"
|
width="100"
|
||||||
label="豆瓣评分">
|
label="豆瓣评分">
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column v-if="filteredRecommendations.some(e => e.detail.note)"
|
<el-table-column v-if="filteredList.some(e => e.detail.note)"
|
||||||
prop="detail.note"
|
prop="detail.note"
|
||||||
label="备注">
|
label="备注">
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
@@ -74,13 +98,12 @@
|
|||||||
<el-button @click.stop="playEvent(scope.row)" type="text">播放</el-button>
|
<el-button @click.stop="playEvent(scope.row)" type="text">播放</el-button>
|
||||||
<el-button @click.stop="shareEvent(scope.row)" type="text">分享</el-button>
|
<el-button @click.stop="shareEvent(scope.row)" type="text">分享</el-button>
|
||||||
<el-button @click.stop="downloadEvent(scope.row)" type="text">下载</el-button>
|
<el-button @click.stop="downloadEvent(scope.row)" type="text">下载</el-button>
|
||||||
<el-button @click.stop="deleteEvent(scope.row)" type="text">删除</el-button>
|
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
</div>
|
</div>
|
||||||
<div class="show-picture" id="star-picture" v-show="viewMode === 'picture'">
|
<div class="show-picture" id="star-picture" v-if="setting.recommendationViewMode === 'picture'">
|
||||||
<Waterfall ref="recommendataionsWaterfall" :list="filteredRecommendations" :gutter="20" :width="240"
|
<Waterfall ref="recommendationsWaterfall" :list="filteredList" :gutter="20" :width="240"
|
||||||
:breakpoints="{
|
:breakpoints="{
|
||||||
1200: { //当屏幕宽度小于等于1200
|
1200: { //当屏幕宽度小于等于1200
|
||||||
rowPerView: 4,
|
rowPerView: 4,
|
||||||
@@ -100,13 +123,12 @@
|
|||||||
<div class="rate" v-if="props.data.rate && props.data.rate !== '暂无评分'">
|
<div class="rate" v-if="props.data.rate && props.data.rate !== '暂无评分'">
|
||||||
<span>{{props.data.rate}}分</span>
|
<span>{{props.data.rate}}分</span>
|
||||||
</div>
|
</div>
|
||||||
<img style="width: 100%" :src="props.data.detail.pic" alt="" @load="$refs.recommendataionsWaterfall.refresh()" @click="detailEvent(props.data)">
|
<img style="width: 100%" :src="props.data.detail.pic" alt="" @load="$refs.recommendationsWaterfall.refresh()" @click="detailEvent(props.data)">
|
||||||
<div class="operate">
|
<div class="operate">
|
||||||
<div class="operate-wrap">
|
<div class="operate-wrap">
|
||||||
<span class="o-play" @click="playEvent(props.data)">播放</span>
|
<span class="o-play" @click="playEvent(props.data)">播放</span>
|
||||||
<span class="o-share" @click="shareEvent(props.data)">分享</span>
|
<span class="o-share" @click="shareEvent(props.data)">分享</span>
|
||||||
<span class="o-star" @click="downloadEvent(props.data)">下载</span>
|
<span class="o-star" @click="downloadEvent(props.data)">下载</span>
|
||||||
<span class="o-star" @click="deleteEvent(props.data)">删除</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -126,24 +148,63 @@
|
|||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import { mapMutations } from 'vuex'
|
import { mapMutations } from 'vuex'
|
||||||
import { history, recommendation, setting } from '../lib/dexie'
|
import { history, recommendation, setting, sites, cachedMovies } from '../lib/dexie'
|
||||||
import zy from '../lib/site/tools'
|
import zy from '../lib/site/tools'
|
||||||
import Waterfall from 'vue-waterfall-plugin'
|
import Waterfall from 'vue-waterfall-plugin'
|
||||||
|
import axios from 'axios'
|
||||||
const { clipboard } = require('electron')
|
const { clipboard } = require('electron')
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'recommendations',
|
name: 'recommendations',
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
recommendations: [],
|
recommendations: [],
|
||||||
sites: [],
|
sites: [],
|
||||||
viewMode: 'picture',
|
|
||||||
loading: false,
|
loading: false,
|
||||||
types: [],
|
types: [],
|
||||||
selectedTypes: [],
|
|
||||||
areas: [],
|
areas: [],
|
||||||
|
filteredList: [],
|
||||||
|
// 不同推荐
|
||||||
|
recommendationsDefault: [],
|
||||||
|
recommendationTypes: ['豆瓣热门电影', '豆瓣高分电影', '豆瓣华语电影', '豆瓣冷门佳片', '豆瓣热门剧集', '豆瓣热门美剧', '豆瓣热门英剧', '豆瓣热门国产剧', '豆瓣热门综艺', '豆瓣热门动漫', '豆瓣热门纪录片', '豆瓣热门动画电影'],
|
||||||
|
selectedRecommendationType: '豆瓣热门电影',
|
||||||
|
// Toolbar
|
||||||
|
showToolbar: false,
|
||||||
selectedAreas: [],
|
selectedAreas: [],
|
||||||
|
selectedTypes: [],
|
||||||
sortKeyword: '',
|
sortKeyword: '',
|
||||||
sortKeywords: ['上映', '评分', '默认']
|
sortKeywords: ['按片名', '按上映年份', '按更新时间', '按评分'],
|
||||||
|
selectedYears: { start: 0, end: new Date().getFullYear() },
|
||||||
|
// 缓存数据
|
||||||
|
localCachedMovies: [],
|
||||||
|
// 豆瓣
|
||||||
|
douban: {
|
||||||
|
page_limit: 50,
|
||||||
|
hotMoviePageStart: 0,
|
||||||
|
hotmovie: [],
|
||||||
|
hotTVPageStart: 0,
|
||||||
|
hotTV: [],
|
||||||
|
highRateMoviePageStart: 0,
|
||||||
|
highRateMovie: [],
|
||||||
|
hotAnimePageStart: 0,
|
||||||
|
hotAnime: [],
|
||||||
|
hotDocumentaryPageStart: 0,
|
||||||
|
hotDocumentary: [],
|
||||||
|
hotTVShowPageStart: 0,
|
||||||
|
hotTVShow: [],
|
||||||
|
hotCartonMoviePageStart: 0,
|
||||||
|
hotCartonMovie: [],
|
||||||
|
hotAmericanTVSeriesPageStart: 0,
|
||||||
|
hotAmericanTVSeries: [],
|
||||||
|
hotBritishTVSeriesPageStart: 0,
|
||||||
|
hotBritishTVSeries: [],
|
||||||
|
hotChineseTVSeriesPageStart: 0,
|
||||||
|
hotChineseTVSeries: [],
|
||||||
|
goodButNotHotMoviesPageStart: 0,
|
||||||
|
goodButNotHotMovies: [],
|
||||||
|
chineseMoviesPageStart: 0,
|
||||||
|
chineseMovies: []
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
@@ -182,59 +243,157 @@ export default {
|
|||||||
this.SET_SHARE(val)
|
this.SET_SHARE(val)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
filteredRecommendations () {
|
setting: {
|
||||||
var filteredData = this.recommendations.filter(x => (this.selectedAreas.length === 0) || this.selectedAreas.includes(x.detail.area))
|
get () {
|
||||||
filteredData = filteredData.filter(x => (this.selectedTypes.length === 0) || this.selectedTypes.includes(x.detail.type))
|
return this.$store.getters.getSetting
|
||||||
return filteredData
|
},
|
||||||
|
set (val) {
|
||||||
|
this.SET_SETTING(val)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
view () {
|
view () {
|
||||||
if (this.view === 'Recommendation') {
|
if (this.view === 'Recommendation') {
|
||||||
this.getRecommendations()
|
if (this.$refs.recommendationsWaterfall) this.$refs.recommendationsWaterfall.resize()
|
||||||
if (this.$refs.recommendataionsWaterfall) {
|
|
||||||
this.$refs.recommendataionsWaterfall.refresh()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
sortKeyword () {
|
recommendations: {
|
||||||
switch (this.sortKeyword) {
|
handler (recommendations) {
|
||||||
case '上映':
|
this.areas = [...new Set(recommendations.map(ele => ele.detail.area))].filter(x => x)
|
||||||
this.recommendations = this.recommendations.sort(function (a, b) {
|
this.types = [...new Set(recommendations.map(ele => ele.detail.type))].filter(x => x)
|
||||||
return b.detail.year - a.detail.year
|
this.refreshFilteredList()
|
||||||
})
|
},
|
||||||
break
|
deep: true
|
||||||
case '评分':
|
|
||||||
this.recommendations.sort(function (a, b) {
|
|
||||||
return b.rate - a.rate
|
|
||||||
})
|
|
||||||
break
|
|
||||||
case '默认':
|
|
||||||
this.recommendations.sort(function (a, b) {
|
|
||||||
return b.id - a.id
|
|
||||||
})
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapMutations(['SET_VIEW', 'SET_DETAIL', 'SET_VIDEO', 'SET_SHARE']),
|
...mapMutations(['SET_VIEW', 'SET_DETAIL', 'SET_VIDEO', 'SET_SHARE', 'SET_SETTING']),
|
||||||
detailEvent (e) {
|
changeRecommendationTypeEvent () {
|
||||||
this.detail = {
|
if (this.selectedRecommendationType === '作者推荐') {
|
||||||
show: true,
|
this.recommendations = this.recommendationsDefault
|
||||||
key: e.key,
|
} else {
|
||||||
info: {
|
if (this.selectedRecommendationType === '豆瓣热门电影') {
|
||||||
id: e.ids,
|
this.recommendations = [...this.douban.hotmovie]
|
||||||
name: e.name
|
}
|
||||||
|
if (this.selectedRecommendationType === '豆瓣高分电影') {
|
||||||
|
this.recommendations = [...this.douban.highRateMovie]
|
||||||
|
}
|
||||||
|
if (this.selectedRecommendationType === '豆瓣热门剧集') {
|
||||||
|
this.recommendations = [...this.douban.hotTV]
|
||||||
|
}
|
||||||
|
if (this.selectedRecommendationType === '豆瓣热门美剧') {
|
||||||
|
this.recommendations = [...this.douban.hotAmericanTVSeries]
|
||||||
|
}
|
||||||
|
if (this.selectedRecommendationType === '豆瓣热门英剧') {
|
||||||
|
this.recommendations = [...this.douban.hotBritishTVSeries]
|
||||||
|
}
|
||||||
|
if (this.selectedRecommendationType === '豆瓣热门国产剧') {
|
||||||
|
this.recommendations = [...this.douban.hotChineseTVSeries]
|
||||||
|
}
|
||||||
|
if (this.selectedRecommendationType === '豆瓣热门动漫') {
|
||||||
|
this.recommendations = [...this.douban.hotAnime]
|
||||||
|
}
|
||||||
|
if (this.selectedRecommendationType === '豆瓣热门纪录片') {
|
||||||
|
this.recommendations = [...this.douban.hotDocumentary]
|
||||||
|
}
|
||||||
|
if (this.selectedRecommendationType === '豆瓣热门综艺') {
|
||||||
|
this.recommendations = [...this.douban.hotTVShow]
|
||||||
|
}
|
||||||
|
if (this.selectedRecommendationType === '豆瓣热门动画电影') {
|
||||||
|
this.recommendations = [...this.douban.hotCartonMovie]
|
||||||
|
}
|
||||||
|
if (this.selectedRecommendationType === '豆瓣冷门佳片') {
|
||||||
|
this.recommendations = [...this.douban.goodButNotHotMovies]
|
||||||
|
}
|
||||||
|
if (this.selectedRecommendationType === '豆瓣华语电影') {
|
||||||
|
this.recommendations = [...this.douban.chineseMovies]
|
||||||
|
}
|
||||||
|
if (this.recommendations.length === 0) {
|
||||||
|
this.updateDoubanRecommendationsEvent()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
getRecommendationsDoubanMovieOrTV (doubanUrl) {
|
||||||
|
axios.get(doubanUrl).then(res => {
|
||||||
|
if (res.data) {
|
||||||
|
res.data.subjects.forEach(element => {
|
||||||
|
const localCachedMovie = this.localCachedMovies.find(e => e.key === this.sites[0].key && e.name === element.title)
|
||||||
|
if (localCachedMovie) {
|
||||||
|
this.updateDoubanRecommendataions(localCachedMovie)
|
||||||
|
} else {
|
||||||
|
this.searchAndCacheMovie(element)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
updateDoubanRecommendataions (movie) {
|
||||||
|
this.recommendations.push(movie)
|
||||||
|
if (this.selectedRecommendationType === '豆瓣热门电影') {
|
||||||
|
this.douban.hotmovie.push(movie)
|
||||||
|
}
|
||||||
|
if (this.selectedRecommendationType === '豆瓣高分电影') {
|
||||||
|
this.douban.highRateMovie.push(movie)
|
||||||
|
}
|
||||||
|
if (this.selectedRecommendationType === '豆瓣热门剧集') {
|
||||||
|
this.douban.hotTV.push(movie)
|
||||||
|
}
|
||||||
|
if (this.selectedRecommendationType === '豆瓣热门美剧') {
|
||||||
|
this.douban.hotAmericanTVSeries.push(movie)
|
||||||
|
}
|
||||||
|
if (this.selectedRecommendationType === '豆瓣热门英剧') {
|
||||||
|
this.douban.hotBritishTVSeries.push(movie)
|
||||||
|
}
|
||||||
|
if (this.selectedRecommendationType === '豆瓣热门国产剧') {
|
||||||
|
this.douban.hotChineseTVSeries.push(movie)
|
||||||
|
}
|
||||||
|
if (this.selectedRecommendationType === '豆瓣热门动漫') {
|
||||||
|
this.douban.hotAnime.push(movie)
|
||||||
|
}
|
||||||
|
if (this.selectedRecommendationType === '豆瓣热门纪录片') {
|
||||||
|
this.douban.hotDocumentary.push(movie)
|
||||||
|
}
|
||||||
|
if (this.selectedRecommendationType === '豆瓣热门综艺') {
|
||||||
|
this.douban.hotTVShow.push(movie)
|
||||||
|
}
|
||||||
|
if (this.selectedRecommendationType === '豆瓣热门动画电影') {
|
||||||
|
this.douban.hotCartonMovie.push(movie)
|
||||||
|
}
|
||||||
|
if (this.selectedRecommendationType === '豆瓣冷门佳片') {
|
||||||
|
this.douban.goodButNotHotMovies.push(movie)
|
||||||
|
}
|
||||||
|
if (this.selectedRecommendationType === '豆瓣华语电影') {
|
||||||
|
this.douban.chineseMovies.push(movie)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
searchAndCacheMovie (element) {
|
||||||
|
zy.searchFirstDetail(this.sites[0].key, element.title).then(detailRes => {
|
||||||
|
if (detailRes) {
|
||||||
|
const doc = {
|
||||||
|
key: this.sites[0].key,
|
||||||
|
ids: detailRes.id,
|
||||||
|
site: this.sites[0],
|
||||||
|
name: detailRes.name,
|
||||||
|
detail: detailRes,
|
||||||
|
rate: element.rate
|
||||||
|
}
|
||||||
|
this.updateDoubanRecommendataions(doc)
|
||||||
|
this.localCachedMovies.push(doc)
|
||||||
|
cachedMovies.add(doc)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
updateEvent () {
|
updateEvent () {
|
||||||
|
if (this.selectedRecommendationType === '作者推荐') {
|
||||||
|
this.updateAuthorRecommendataions()
|
||||||
|
} else {
|
||||||
|
this.updateDoubanRecommendationsEvent()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
updateAuthorRecommendataions () {
|
||||||
const url = 'https://raw.githubusercontent.com/cuiocean/ZY-Player-Resources/main/Recommendations/Recommendations.json'
|
const url = 'https://raw.githubusercontent.com/cuiocean/ZY-Player-Resources/main/Recommendations/Recommendations.json'
|
||||||
this.loading = true
|
this.loading = true
|
||||||
const axios = require('axios')
|
|
||||||
axios.get(url).then(res => {
|
axios.get(url).then(res => {
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
if (res.data.length > 0) {
|
if (res.data.length > 0) {
|
||||||
@@ -251,6 +410,128 @@ export default {
|
|||||||
this.$message.warning('最新的推荐数据保存在Github上,请考虑使用代理或者等待下一版本内置数据更新.')
|
this.$message.warning('最新的推荐数据保存在Github上,请考虑使用代理或者等待下一版本内置数据更新.')
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
updateDoubanRecommendationsEvent () {
|
||||||
|
let doubanUrl = ''
|
||||||
|
if (this.selectedRecommendationType === '豆瓣热门电影') {
|
||||||
|
doubanUrl = `https://movie.douban.com/j/search_subjects?type=movie&tag=热门&sort=recommend&page_limit=${this.douban.page_limit}&page_start=${this.douban.hotMoviePageStart}`
|
||||||
|
this.douban.hotMoviePageStart = this.douban.hotMoviePageStart + this.douban.page_limit
|
||||||
|
}
|
||||||
|
if (this.selectedRecommendationType === '豆瓣热门剧集') {
|
||||||
|
doubanUrl = `https://movie.douban.com/j/search_subjects?type=tv&tag=热门&sort=recommend&page_limit=${this.douban.page_limit}&page_start=${this.douban.hotTVPageStart}`
|
||||||
|
this.douban.hotTVPageStart = this.douban.hotTVPageStart + this.douban.page_limit
|
||||||
|
}
|
||||||
|
if (this.selectedRecommendationType === '豆瓣热门美剧') {
|
||||||
|
doubanUrl = `https://movie.douban.com/j/search_subjects?type=tv&tag=美剧&sort=recommend&page_limit=${this.douban.page_limit}&page_start=${this.douban.hotCartonMoviePageStart}`
|
||||||
|
this.douban.hotCartonMoviePageStart = this.douban.hotCartonMoviePageStart + this.douban.page_limit
|
||||||
|
}
|
||||||
|
if (this.selectedRecommendationType === '豆瓣热门英剧') {
|
||||||
|
doubanUrl = `https://movie.douban.com/j/search_subjects?type=tv&tag=英剧&sort=recommend&page_limit=${this.douban.page_limit}&page_start=${this.douban.hotBritishTVSeriesPageStart}`
|
||||||
|
this.douban.hotBritishTVSeriesPageStart = this.douban.hotBritishTVSeriesPageStart + this.douban.page_limit
|
||||||
|
}
|
||||||
|
if (this.selectedRecommendationType === '豆瓣热门国产剧') {
|
||||||
|
doubanUrl = `https://movie.douban.com/j/search_subjects?type=tv&tag=国产剧&sort=recommend&page_limit=${this.douban.page_limit}&page_start=${this.douban.hotChineseTVSeriesPageStart}`
|
||||||
|
this.douban.hotChineseTVSeriesPageStart = this.douban.hotChineseTVSeriesPageStart + this.douban.page_limit
|
||||||
|
}
|
||||||
|
if (this.selectedRecommendationType === '豆瓣高分电影') {
|
||||||
|
doubanUrl = `https://movie.douban.com/j/search_subjects?type=movie&tag=豆瓣高分&sort=recommend&page_limit=${this.douban.page_limit}&page_start=${this.douban.highRateMoviePageStart}`
|
||||||
|
this.douban.highRateMoviePageStart = this.douban.highRateMoviePageStart + this.douban.page_limit
|
||||||
|
}
|
||||||
|
if (this.selectedRecommendationType === '豆瓣热门动漫') {
|
||||||
|
doubanUrl = `https://movie.douban.com/j/search_subjects?type=tv&tag=日本动画&sort=recommend&page_limit=${this.douban.page_limit}&page_start=${this.douban.hotAnimePageStart}`
|
||||||
|
this.douban.hotAnimePageStart = this.douban.hotAnimePageStart + this.douban.page_limit
|
||||||
|
}
|
||||||
|
if (this.selectedRecommendationType === '豆瓣热门纪录片') {
|
||||||
|
doubanUrl = `https://movie.douban.com/j/search_subjects?type=tv&tag=纪录片&sort=recommend&page_limit=${this.douban.page_limit}&page_start=${this.douban.hotDocumentaryPageStart}`
|
||||||
|
this.douban.hotDocumentaryPageStart = this.douban.hotDocumentaryPageStart + this.douban.page_limit
|
||||||
|
}
|
||||||
|
if (this.selectedRecommendationType === '豆瓣热门综艺') {
|
||||||
|
doubanUrl = `https://movie.douban.com/j/search_subjects?type=tv&tag=综艺&sort=recommend&page_limit=${this.douban.page_limit}&page_start=${this.douban.hotTVShowPageStart}`
|
||||||
|
this.douban.hotTVShowPageStart = this.douban.hotTVShowPageStart + this.douban.page_limit
|
||||||
|
}
|
||||||
|
if (this.selectedRecommendationType === '豆瓣热门动画电影') {
|
||||||
|
doubanUrl = `https://movie.douban.com/j/search_subjects?type=movie&tag=动画&sort=recommend&page_limit=${this.douban.page_limit}&page_start=${this.douban.hotCartonMoviePageStart}`
|
||||||
|
this.douban.hotCartonMoviePageStart = this.douban.hotCartonMoviePageStart + this.douban.page_limit
|
||||||
|
}
|
||||||
|
if (this.selectedRecommendationType === '豆瓣冷门佳片') {
|
||||||
|
doubanUrl = `https://movie.douban.com/j/search_subjects?type=movie&tag=冷门佳片&sort=recommend&page_limit=${this.douban.page_limit}&page_start=${this.douban.goodButNotHotMoviesPageStart}`
|
||||||
|
this.douban.goodButNotHotMoviesPageStart = this.douban.goodButNotHotMoviesPageStart + this.douban.page_limit
|
||||||
|
}
|
||||||
|
if (this.selectedRecommendationType === '豆瓣华语电影') {
|
||||||
|
doubanUrl = `https://movie.douban.com/j/search_subjects?type=movie&tag=华语&sort=recommend&page_limit=${this.douban.page_limit}&page_start=${this.douban.chineseMoviesPageStart}`
|
||||||
|
this.douban.chineseMoviesPageStart = this.douban.chineseMoviesPageStart + this.douban.page_limit
|
||||||
|
}
|
||||||
|
this.getRecommendationsDoubanMovieOrTV(doubanUrl)
|
||||||
|
},
|
||||||
|
toggleViewMode () {
|
||||||
|
this.setting.recommendationViewMode = this.setting.recommendationViewMode === 'picture' ? 'table' : 'picture'
|
||||||
|
if (this.setting.recommendationViewMode === 'table') {
|
||||||
|
setTimeout(() => { this.rowDrop() }, 100)
|
||||||
|
} else {
|
||||||
|
setTimeout(() => { if (this.$refs.recommendationsWaterfall) this.$refs.recommendationsWaterfall.refresh() }, 700)
|
||||||
|
}
|
||||||
|
setting.find().then(res => {
|
||||||
|
res.recommendationViewMode = this.setting.recommendationViewMode
|
||||||
|
setting.update(res)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
backTop () {
|
||||||
|
if (this.setting.recommendationViewMode === 'picture') {
|
||||||
|
document.getElementById('recommendations-body').scrollTop = 0
|
||||||
|
} else {
|
||||||
|
this.$refs.recommendationsTable.bodyWrapper.scrollTop = 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
refreshFilteredList () {
|
||||||
|
if (!this.showToolbar) {
|
||||||
|
this.sortKeyword = ''
|
||||||
|
this.selectedAreas = []
|
||||||
|
this.selectedSearchClassNames = []
|
||||||
|
this.selectedYears.start = 0
|
||||||
|
this.selectedYears.end = new Date().getFullYear()
|
||||||
|
this.filteredList = this.recommendations
|
||||||
|
} else {
|
||||||
|
let filteredData = this.recommendations
|
||||||
|
filteredData = filteredData.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))
|
||||||
|
filteredData = filteredData.filter(res => res.detail.year >= this.selectedYears.start)
|
||||||
|
filteredData = filteredData.filter(res => res.detail.year <= this.selectedYears.end)
|
||||||
|
switch (this.sortKeyword) {
|
||||||
|
case '按上映年份':
|
||||||
|
filteredData.sort(function (a, b) {
|
||||||
|
return b.detail.year - a.detail.year
|
||||||
|
})
|
||||||
|
break
|
||||||
|
case '按片名':
|
||||||
|
filteredData.sort(function (a, b) {
|
||||||
|
return a.detail.name.localeCompare(b.detail.name, 'zh')
|
||||||
|
})
|
||||||
|
break
|
||||||
|
case '按更新时间':
|
||||||
|
filteredData.sort(function (a, b) {
|
||||||
|
return new Date(b.detail.last) - new Date(a.detail.last)
|
||||||
|
})
|
||||||
|
break
|
||||||
|
case '按评分':
|
||||||
|
filteredData.sort(function (a, b) {
|
||||||
|
return b.rate - a.rate
|
||||||
|
})
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
this.filteredList = filteredData
|
||||||
|
}
|
||||||
|
},
|
||||||
|
detailEvent (e) {
|
||||||
|
this.detail = {
|
||||||
|
show: true,
|
||||||
|
key: e.key,
|
||||||
|
info: {
|
||||||
|
id: e.ids,
|
||||||
|
name: e.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
async playEvent (e) {
|
async playEvent (e) {
|
||||||
const db = await history.find({ site: e.key, ids: e.ids })
|
const db = await history.find({ site: e.key, ids: e.ids })
|
||||||
if (db) {
|
if (db) {
|
||||||
@@ -260,89 +541,62 @@ export default {
|
|||||||
}
|
}
|
||||||
this.view = 'Play'
|
this.view = 'Play'
|
||||||
},
|
},
|
||||||
deleteEvent (e) {
|
|
||||||
recommendation.remove(e.id).then(res => {
|
|
||||||
if (res) {
|
|
||||||
this.$message.warning('删除失败')
|
|
||||||
}
|
|
||||||
this.getRecommendations()
|
|
||||||
})
|
|
||||||
},
|
|
||||||
shareEvent (e) {
|
shareEvent (e) {
|
||||||
this.share = {
|
this.share = {
|
||||||
show: true,
|
show: true,
|
||||||
key: e.key,
|
key: e.key,
|
||||||
info: e
|
info: e.detail
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
downloadEvent (e) {
|
async downloadEvent (e) {
|
||||||
zy.download(e.key, e.ids).then(res => {
|
const db = await history.find({ site: e.key, ids: e.ids })
|
||||||
if (res) {
|
let videoFlag
|
||||||
const text = res.m3u8List
|
if (db) videoFlag = db.videoFlag
|
||||||
if (text) {
|
zy.download(e.key, e.ids, videoFlag).then(res => {
|
||||||
const list = text.split('#')
|
clipboard.writeText(res.downloadUrls)
|
||||||
let downloadUrl = ''
|
this.$message.success(res.info)
|
||||||
for (const i of list) {
|
}).catch((err) => {
|
||||||
const url = encodeURI(i.split('$')[1])
|
this.$message.error(err.info)
|
||||||
downloadUrl += (url + '\n')
|
|
||||||
}
|
|
||||||
clipboard.writeText(downloadUrl)
|
|
||||||
this.$message.success('『MP4』格式的链接已复制, 快去下载吧!')
|
|
||||||
} else {
|
|
||||||
this.$message.warning('没有查询到下载链接.')
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
zy.detail(e.key, e.ids).then(res => {
|
|
||||||
const list = [...res.m3u8List]
|
|
||||||
let downloadUrl = ''
|
|
||||||
for (const i of list) {
|
|
||||||
const url = encodeURI(i.split('$')[1])
|
|
||||||
downloadUrl += (url + '\n')
|
|
||||||
}
|
|
||||||
clipboard.writeText(downloadUrl)
|
|
||||||
this.$message.success('『M3U8』格式的链接已复制, 快去下载吧!')
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
getRecommendations () {
|
getRecommendations () {
|
||||||
recommendation.all().then(res => {
|
this.recommendationsDefault = []
|
||||||
this.recommendations = res.sort(function (a, b) {
|
this.changeRecommendationTypeEvent()
|
||||||
return b.id - a.id
|
this.getFilterData()
|
||||||
})
|
|
||||||
this.getFilterData()
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
getFilterData () {
|
getFilterData () {
|
||||||
this.types = [...new Set(this.recommendations.map(ele => ele.detail.type))].filter(x => x)
|
this.types = [...new Set(this.recommendations.map(ele => ele.detail.type))].filter(x => x)
|
||||||
this.areas = [...new Set(this.recommendations.map(ele => ele.detail.area))].filter(x => x)
|
this.areas = [...new Set(this.recommendations.map(ele => ele.detail.area))].filter(x => x)
|
||||||
},
|
},
|
||||||
getViewMode () {
|
updateViewMode () {
|
||||||
|
setTimeout(() => { if (this.$refs.recommendationsWaterfall) this.$refs.recommendationsWaterfall.refresh() }, 700)
|
||||||
setting.find().then(res => {
|
setting.find().then(res => {
|
||||||
this.viewMode = res.recommendationViewMode
|
res.recommendationViewMode = this.setting.recommendationViewMode
|
||||||
|
setting.update(res)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
updateViewMode () {
|
getAllSites () {
|
||||||
setting.find().then(res => {
|
sites.all().then(res => {
|
||||||
res.recommendationViewMode = this.viewMode
|
if (res.length > 0) {
|
||||||
setting.update(res)
|
this.sites = res.filter(item => item.isActive)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
getCachedMovies () {
|
||||||
|
cachedMovies.all().then(res => {
|
||||||
|
this.localCachedMovies = res
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created () {
|
created () {
|
||||||
|
this.getAllSites()
|
||||||
this.getRecommendations()
|
this.getRecommendations()
|
||||||
this.getViewMode()
|
this.getCachedMovies()
|
||||||
},
|
},
|
||||||
mounted () {
|
mounted () {
|
||||||
window.addEventListener('resize', () => {
|
addEventListener('resize', () => {
|
||||||
if (this.$refs.recommendataionsWaterfall && this.view === 'Recommendation') {
|
setTimeout(() => { if (this.$refs.recommendationsWaterfall) this.$refs.recommendationsWaterfall.resize() }, 500)
|
||||||
this.$refs.recommendataionsWaterfall.resize()
|
})
|
||||||
this.$refs.recommendataionsWaterfall.refresh()
|
|
||||||
setTimeout(() => {
|
|
||||||
this.$refs.recommendataionsWaterfall.refresh()
|
|
||||||
}, 500)
|
|
||||||
}
|
|
||||||
}, false)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -5,22 +5,8 @@
|
|||||||
<div class="info">
|
<div class="info">
|
||||||
<a @click="linkOpen('http://zyplayer.fun/')">官网</a>
|
<a @click="linkOpen('http://zyplayer.fun/')">官网</a>
|
||||||
<a @click="linkOpen('https://github.com/Hunlongyu/ZY-Player')">Github</a>
|
<a @click="linkOpen('https://github.com/Hunlongyu/ZY-Player')">Github</a>
|
||||||
<a @click="linkOpen('https://github.com/Hunlongyu/ZY-Player/issues')">当前版本v{{pkg.version}} 反馈</a>
|
<a @click="linkOpen('https://github.com/Hunlongyu/ZY-Player/discussions/776')">软件完全免费,如遇收费,请立即给差评并退费!</a>
|
||||||
<a style="color:#38dd77" @click="quitAndInstall()" v-show="latestVersion !== pkg.version" >最新版本v{{latestVersion}}</a>
|
<a style="color:#38dd77" @click="openUpdate()" v-show="update.find" >最新版本v{{update.version}}</a>
|
||||||
</div>
|
|
||||||
<div class="view">
|
|
||||||
<div class="title">视图</div>
|
|
||||||
<div class="view-box">
|
|
||||||
<div class="zy-select" @mouseleave="show.view = false">
|
|
||||||
<div class="vs-placeholder" @click="show.view = true">默认视图</div>
|
|
||||||
<div class="vs-options" v-show="show.view">
|
|
||||||
<ul class="zy-scroll">
|
|
||||||
<li :class="d.view === 'picture' ? 'active' : ''" @click="changeView('picture')">海报</li>
|
|
||||||
<li :class="d.view === 'table' ? 'active' : ''" @click="changeView('table')">列表</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="shortcut">
|
<div class="shortcut">
|
||||||
<div class="title">快捷键</div>
|
<div class="title">快捷键</div>
|
||||||
@@ -40,13 +26,16 @@
|
|||||||
<div class="zy-select">
|
<div class="zy-select">
|
||||||
<div class="vs-placeholder vs-noAfter" @click="impShortcut">导入</div>
|
<div class="vs-placeholder vs-noAfter" @click="impShortcut">导入</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="zy-select">
|
||||||
|
<div class="vs-placeholder vs-noAfter" @click="resetShortcut">重置</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="shortcut">
|
<div class="shortcut" title="清理缓存后图片资源需重新下载,不建议清理,软件会根据磁盘空间动态管理缓存大小">
|
||||||
<div class="title">缓存</div>
|
<div class="title">缓存</div>
|
||||||
<div class="shortcut-box">
|
<div class="shortcut-box">
|
||||||
<div class="zy-select">
|
<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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -84,6 +73,10 @@
|
|||||||
<div class="zy-input">
|
<div class="zy-input">
|
||||||
<input type="checkbox" v-model = "d.autocleanWhenIptvCheck" @change="updateSettingEvent"> 检测时自动清理无效源
|
<input type="checkbox" v-model = "d.autocleanWhenIptvCheck" @change="updateSettingEvent"> 检测时自动清理无效源
|
||||||
</div>
|
</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>
|
</div>
|
||||||
<div class="site">
|
<div class="site">
|
||||||
@@ -92,8 +85,11 @@
|
|||||||
<div class="zy-select">
|
<div class="zy-select">
|
||||||
<div class="vs-placeholder vs-noAfter" @click="editSitesEvent">编辑源</div>
|
<div class="vs-placeholder vs-noAfter" @click="editSitesEvent">编辑源</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="zy-input" @click="toggleExcludeRootClasses">
|
<div class="zy-select">
|
||||||
<input type="checkbox" v-model = "d.excludeRootClasses" @change="updateSettingEvent"> 屏蔽主分类
|
<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>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -112,6 +108,15 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="site">
|
||||||
|
<div class="title">窗口及播放</div>
|
||||||
|
<div class="site-box">
|
||||||
|
<div class="zy-input">
|
||||||
|
<input type="checkbox" v-model = "d.restoreWindowPositionAndSize" @change="updateSettingEvent"> 记录并恢复窗口位置和大小
|
||||||
|
<input type="checkbox" v-model = "d.pauseWhenMinimize" @change="updateSettingEvent"> 最小化时暂停播放
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="theme">
|
<div class="theme">
|
||||||
<div class="title">主题</div>
|
<div class="title">主题</div>
|
||||||
<div class="theme-box">
|
<div class="theme-box">
|
||||||
@@ -141,13 +146,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="qrcode">
|
|
||||||
<div class="title">请作者吃辣条</div>
|
|
||||||
<div class="qrcode-box">
|
|
||||||
<img class="qrcode-item" src="../assets/image/wepay-hunlongyu.png">
|
|
||||||
<img class="qrcode-item" src="../assets/image/wepay_cuiocean.jpg">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="clearDB">
|
<div class="clearDB">
|
||||||
<span @click="clearDBEvent" class="clearBtn">软件重置</span>
|
<span @click="clearDBEvent" class="clearBtn">软件重置</span>
|
||||||
<span @click="changePasswordEvent" class="clearBtn">设置密码</span>
|
<span @click="changePasswordEvent" class="clearBtn">设置密码</span>
|
||||||
@@ -157,6 +155,34 @@
|
|||||||
<span>所有资源来自网上, 该软件不参与任何制作, 上传, 储存等内容, 禁止传播违法资源. 该软件仅供学习参考, 请于安装后24小时内删除.</span>
|
<span>所有资源来自网上, 该软件不参与任何制作, 上传, 储存等内容, 禁止传播违法资源. 该软件仅供学习参考, 请于安装后24小时内删除.</span>
|
||||||
</div>
|
</div>
|
||||||
</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="resetDefaultParseURL">重置</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="resetDefaultSitesDataURL">重置</el-button>
|
||||||
|
<el-button type="primary" @click="configSitesDataURL">确定</el-button>
|
||||||
|
</span>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
<div> <!-- 输入密码页面 -->
|
<div> <!-- 输入密码页面 -->
|
||||||
<el-dialog :visible.sync="show.checkPasswordDialog" v-if='show.checkPasswordDialog' :append-to-body="true" @close="closeDialog" width="300px">
|
<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">
|
<el-form label-width="75px" label-position="left">
|
||||||
@@ -210,14 +236,28 @@
|
|||||||
</span>
|
</span>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="update" v-if="update.show">
|
||||||
|
<div class="wrapper">
|
||||||
|
<div class="body">
|
||||||
|
<div class="content" v-html="update.html"></div>
|
||||||
|
</div>
|
||||||
|
<div class="footer">
|
||||||
|
<el-button size="small" @click="closeUpdate">关闭</el-button>
|
||||||
|
<el-button size="small" v-show="update.showDownload" @click="startUpdate">更新</el-button>
|
||||||
|
<el-button size="small" v-show="!update.showDownload && !update.downloaded">正在更新...</el-button>
|
||||||
|
<el-button size="small" v-show="update.downloaded" @click="installUpdate">安装</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import { mapMutations } from 'vuex'
|
import { mapMutations } from 'vuex'
|
||||||
import pkg from '../../package.json'
|
import pkg from '../../package.json'
|
||||||
import { setting, sites, shortcut } from '../lib/dexie'
|
import { setting, sites, shortcut } from '../lib/dexie'
|
||||||
import { sites as defaultSites } from '../lib/dexie/initData'
|
import { localKey as defaultShortcuts } from '../lib/dexie/initData'
|
||||||
import { shell, clipboard, remote, ipcRenderer } from 'electron'
|
import { shell, clipboard, ipcRenderer } from 'electron'
|
||||||
|
const remote = require('@electron/remote')
|
||||||
import db from '../lib/dexie/dexie'
|
import db from '../lib/dexie/dexie'
|
||||||
import zy from '../lib/site/tools'
|
import zy from '../lib/site/tools'
|
||||||
export default {
|
export default {
|
||||||
@@ -234,7 +274,9 @@ export default {
|
|||||||
checkPasswordDialog: false,
|
checkPasswordDialog: false,
|
||||||
changePasswordDialog: false,
|
changePasswordDialog: false,
|
||||||
proxy: false,
|
proxy: false,
|
||||||
proxyDialog: false
|
proxyDialog: false,
|
||||||
|
configDefaultParseUrlDialog: false,
|
||||||
|
configSitesDataUrlDialog: false
|
||||||
},
|
},
|
||||||
d: { },
|
d: { },
|
||||||
latestVersion: pkg.version,
|
latestVersion: pkg.version,
|
||||||
@@ -245,6 +287,14 @@ export default {
|
|||||||
scheme: '',
|
scheme: '',
|
||||||
url: '',
|
url: '',
|
||||||
port: ''
|
port: ''
|
||||||
|
},
|
||||||
|
update: {
|
||||||
|
find: false,
|
||||||
|
version: '',
|
||||||
|
show: false,
|
||||||
|
html: '',
|
||||||
|
downloaded: false,
|
||||||
|
showDownload: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -275,13 +325,29 @@ export default {
|
|||||||
setting.find().then(res => {
|
setting.find().then(res => {
|
||||||
this.d = res
|
this.d = res
|
||||||
this.setting = this.d
|
this.setting = this.d
|
||||||
|
if (!this.setting.defaultParseURL) this.configDefaultParseURL()
|
||||||
|
if (!this.setting.sitesDataURL) this.resetDefaultSitesDataURL()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
async getDefaultSites () {
|
||||||
|
const s = await setting.find()
|
||||||
|
zy.getDefaultSites(s.sitesDataURL).then(res => {
|
||||||
|
if (res && typeof res === 'string') {
|
||||||
|
const json = JSON.parse(res)
|
||||||
|
sites.clear().then(sites.bulkAdd(json))
|
||||||
|
}
|
||||||
|
if (res && typeof res === 'object') {
|
||||||
|
sites.clear().then(sites.bulkAdd(res))
|
||||||
|
}
|
||||||
|
}).catch(error => {
|
||||||
|
this.$message.error('获取云端源站失败. ' + error)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
getSites () {
|
getSites () {
|
||||||
sites.all().then(res => {
|
sites.all().then(res => {
|
||||||
if (res.length <= 0) {
|
if (res.length <= 0) {
|
||||||
this.$message.warning('检测到视频源未能正常加载, 即将重置源.')
|
this.$message.warning('检测到视频源未能正常加载, 即将重置源.')
|
||||||
sites.clear().then(sites.bulkAdd(defaultSites).then(this.getSites()))
|
this.getDefaultSites()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
@@ -290,11 +356,6 @@ export default {
|
|||||||
this.shortcutList = res
|
this.shortcutList = res
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
changeView (e) {
|
|
||||||
this.d.view = e
|
|
||||||
this.updateSettingEvent()
|
|
||||||
this.show.view = false
|
|
||||||
},
|
|
||||||
async clearCache () {
|
async clearCache () {
|
||||||
const win = remote.getCurrentWindow()
|
const win = remote.getCurrentWindow()
|
||||||
const ses = win.webContents.session
|
const ses = win.webContents.session
|
||||||
@@ -316,6 +377,24 @@ export default {
|
|||||||
this.d.excludeRootClasses = !this.d.excludeRootClasses
|
this.d.excludeRootClasses = !this.d.excludeRootClasses
|
||||||
this.updateSettingEvent()
|
this.updateSettingEvent()
|
||||||
},
|
},
|
||||||
|
async resetDefaultParseURL () {
|
||||||
|
this.setting.defaultParseURL = 'https://jx.bpba.cc/?v='
|
||||||
|
},
|
||||||
|
async configDefaultParseURL () {
|
||||||
|
if (!this.setting.defaultParseURL) await this.resetDefaultParseURL()
|
||||||
|
this.d.defaultParseURL = this.setting.defaultParseURL?.trim()
|
||||||
|
this.show.configDefaultParseUrlDialog = false
|
||||||
|
this.updateSettingEvent()
|
||||||
|
},
|
||||||
|
resetDefaultSitesDataURL () {
|
||||||
|
this.setting.sitesDataURL = 'https://raw.iqiq.io/Hunlongyu/ZY-Player-Resources/main/Sites/20220713.json'
|
||||||
|
},
|
||||||
|
configSitesDataURL () {
|
||||||
|
if (!this.setting.sitesDataURL) this.resetDefaultSitesDataURL()
|
||||||
|
this.d.sitesDataURL = this.setting.sitesDataURL
|
||||||
|
this.show.configSitesDataUrlDialog = false
|
||||||
|
this.updateSettingEvent()
|
||||||
|
},
|
||||||
selectLocalPlayer () {
|
selectLocalPlayer () {
|
||||||
const options = {
|
const options = {
|
||||||
filters: [
|
filters: [
|
||||||
@@ -326,7 +405,7 @@ export default {
|
|||||||
}
|
}
|
||||||
remote.dialog.showOpenDialog(options).then(result => {
|
remote.dialog.showOpenDialog(options).then(result => {
|
||||||
if (!result.canceled) {
|
if (!result.canceled) {
|
||||||
var playerPath = result.filePaths[0].replace(/\\/g, '/')
|
const playerPath = result.filePaths[0].replace(/\\/g, '/')
|
||||||
this.$message.success('设定第三方播放器路径为:' + result.filePaths[0])
|
this.$message.success('设定第三方播放器路径为:' + result.filePaths[0])
|
||||||
this.d.externalPlayer = playerPath
|
this.d.externalPlayer = playerPath
|
||||||
this.updateSettingEvent()
|
this.updateSettingEvent()
|
||||||
@@ -358,6 +437,8 @@ export default {
|
|||||||
async closeDialog () {
|
async closeDialog () {
|
||||||
this.show.checkPasswordDialog = false
|
this.show.checkPasswordDialog = false
|
||||||
this.show.changePasswordDialog = false
|
this.show.changePasswordDialog = false
|
||||||
|
this.show.configDefaultParseUrlDialog = false
|
||||||
|
this.show.configSitesDataUrlDialog = false
|
||||||
if (this.show.proxyDialog) {
|
if (this.show.proxyDialog) {
|
||||||
this.show.proxyDialog = false
|
this.show.proxyDialog = false
|
||||||
this.setting.proxy.type = 'none'
|
this.setting.proxy.type = 'none'
|
||||||
@@ -416,10 +497,20 @@ export default {
|
|||||||
this.$message.info('已清空原数据')
|
this.$message.info('已清空原数据')
|
||||||
shortcut.add(json).then(e => {
|
shortcut.add(json).then(e => {
|
||||||
this.$message.success('已添加成功')
|
this.$message.success('已添加成功')
|
||||||
this.getSites()
|
this.getShortcut()
|
||||||
|
this.d.shortcutModified = true
|
||||||
|
this.updateSettingEvent()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
resetShortcut () {
|
||||||
|
shortcut.clear().then(shortcut.add(defaultShortcuts)).then(res => {
|
||||||
|
this.getShortcut()
|
||||||
|
this.$message.success('快捷键已重置')
|
||||||
|
this.d.shortcutModified = true
|
||||||
|
this.updateSettingEvent()
|
||||||
|
})
|
||||||
|
},
|
||||||
async changeProxyType (e) {
|
async changeProxyType (e) {
|
||||||
this.d.proxy.type = e
|
this.d.proxy.type = e
|
||||||
if (e === 'manual') {
|
if (e === 'manual') {
|
||||||
@@ -466,20 +557,29 @@ export default {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getLatestVersion () {
|
checkUpdate () {
|
||||||
ipcRenderer.send('checkForUpdate')
|
ipcRenderer.send('checkForUpdate')
|
||||||
ipcRenderer.on('update-available', (e, info) => {
|
ipcRenderer.on('update-available', (e, info) => {
|
||||||
this.latestVersion = info.version
|
this.update.find = true
|
||||||
})
|
this.update.version = info.version
|
||||||
ipcRenderer.on('update-error', () => {
|
this.update.html = info.releaseNotes
|
||||||
this.$message.warning = '更新出错.'
|
|
||||||
})
|
|
||||||
ipcRenderer.on('update-downloaded', () => {
|
|
||||||
this.$message.info = '下载完毕, 退出安装'
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
quitAndInstall () {
|
openUpdate () {
|
||||||
this.$message.success('已开始下载更新,下载完毕后,将自动退出安装。')
|
this.update.show = true
|
||||||
|
},
|
||||||
|
closeUpdate () {
|
||||||
|
this.update.show = false
|
||||||
|
},
|
||||||
|
startUpdate () {
|
||||||
|
this.update.showDownload = false
|
||||||
|
ipcRenderer.send('downloadUpdate')
|
||||||
|
ipcRenderer.on('update-downloaded', () => {
|
||||||
|
this.update.downloaded = true
|
||||||
|
this.$message.success('更新已下载完成!Mac用户须手动点击“安装”,其它系统会在退出后自动安装')
|
||||||
|
})
|
||||||
|
},
|
||||||
|
installUpdate () {
|
||||||
ipcRenderer.send('quitAndInstall')
|
ipcRenderer.send('quitAndInstall')
|
||||||
},
|
},
|
||||||
createContextMenu () {
|
createContextMenu () {
|
||||||
@@ -495,10 +595,10 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
created () {
|
created () {
|
||||||
this.getSites()
|
// this.getSites()
|
||||||
this.getSetting()
|
this.getSetting()
|
||||||
this.getShortcut()
|
this.getShortcut()
|
||||||
this.getLatestVersion()
|
this.checkUpdate()
|
||||||
this.createContextMenu()
|
this.createContextMenu()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -536,17 +636,6 @@ export default {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.view{
|
|
||||||
width: 100%;
|
|
||||||
padding: 20px;
|
|
||||||
margin-top: 20px;
|
|
||||||
.view-box{
|
|
||||||
margin-top: 10px;
|
|
||||||
.zy-select{
|
|
||||||
margin-right: 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.site{
|
.site{
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
@@ -641,5 +730,28 @@ export default {
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: #ff000066;
|
color: #ff000066;
|
||||||
}
|
}
|
||||||
|
.update{
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: rgba(7, 17, 27, 0.7);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
.wrapper{
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 20px 50px 40px;
|
||||||
|
border-radius: 4px;
|
||||||
|
max-width: 500px;
|
||||||
|
max-height: 90%;
|
||||||
|
overflow: auto;
|
||||||
|
.footer{
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="share" id="share" @click="shareClickEvent" v-on-clickaway="shareClickEvent">
|
<div class="share" id="share" @click="shareClickEvent" v-clickoutside="shareClickEvent">
|
||||||
<div class="left">
|
<div class="left">
|
||||||
<img :src="pic" alt="" @load="picLoadEvent">
|
<img :src="share.info.pic" alt="">
|
||||||
</div>
|
</div>
|
||||||
<div class="right" id="right">
|
<div class="right" id="right">
|
||||||
<div class="title">{{ share.info.name }}</div>
|
<div class="title">{{ share.info.name }}</div>
|
||||||
<qrcode-vue id="qr" :value="link" :size="160" level="L" />
|
<qrcode-vue v-if="link !== ''" id="qr" :value="link" :size="160" level="L" />
|
||||||
<div class="tips">
|
<div class="tips">
|
||||||
<p>长按二维码,识别播放。</p>
|
<p>长按二维码,识别播放。</p>
|
||||||
<p><img src="@/assets/image/logo.png"></p>
|
<p><img src="@/assets/image/logo.png"></p>
|
||||||
@@ -22,7 +22,7 @@ import { mapMutations } from 'vuex'
|
|||||||
import QrcodeVue from 'qrcode.vue'
|
import QrcodeVue from 'qrcode.vue'
|
||||||
import html2canvas from 'html2canvas'
|
import html2canvas from 'html2canvas'
|
||||||
import zy from '../lib/site/tools'
|
import zy from '../lib/site/tools'
|
||||||
import { directive as onClickaway } from 'vue-clickaway'
|
import Clickoutside from 'element-ui/src/utils/clickoutside'
|
||||||
const { clipboard, nativeImage } = require('electron')
|
const { clipboard, nativeImage } = require('electron')
|
||||||
export default {
|
export default {
|
||||||
name: 'share',
|
name: 'share',
|
||||||
@@ -45,6 +45,14 @@ export default {
|
|||||||
set (val) {
|
set (val) {
|
||||||
this.SET_SHARE(val)
|
this.SET_SHARE(val)
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
DetailCache: {
|
||||||
|
get () {
|
||||||
|
return this.$store.getters.getDetailCache
|
||||||
|
},
|
||||||
|
set (val) {
|
||||||
|
this.SET_DetailCache(val)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
@@ -58,36 +66,43 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
directives: {
|
directives: {
|
||||||
onClickaway: onClickaway
|
Clickoutside
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapMutations(['SET_SHARE']),
|
...mapMutations(['SET_SHARE', 'SET_DetailCache']),
|
||||||
shareClickEvent () {
|
shareClickEvent () {
|
||||||
this.share = {
|
this.share = {
|
||||||
show: false,
|
show: false,
|
||||||
info: {}
|
info: {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getDetail () {
|
async getUrl (index) {
|
||||||
this.loading = true
|
|
||||||
const id = this.share.info.ids || this.share.info.id
|
const id = this.share.info.ids || this.share.info.id
|
||||||
zy.detail(this.share.key, id).then(res => {
|
const cacheKey = this.share.key + '@' + id
|
||||||
if (res) {
|
let res = this.DetailCache[cacheKey]
|
||||||
this.pic = res.pic
|
if (!this.DetailCache[cacheKey]) {
|
||||||
var m3u8List = res.m3u8List
|
res = await zy.detail(this.share.key, id)
|
||||||
const url = m3u8List[1]
|
this.DetailCache[cacheKey] = res
|
||||||
this.link = 'http://zyplayer.fun/player/player.html?url=' + url + '&title=' + this.share.info.name
|
}
|
||||||
}
|
if (res) {
|
||||||
this.loading = false
|
const url = res.fullList[0].list[index]
|
||||||
})
|
return url.includes('$') ? url.split('$')[1] : url
|
||||||
|
}
|
||||||
},
|
},
|
||||||
picLoadEvent () {
|
async getDetail () {
|
||||||
const dom = document.getElementById('share')
|
this.loading = true
|
||||||
html2canvas(dom, { useCORS: true, allowTaint: true }).then(res => {
|
const index = this.share.index || 0
|
||||||
const png = res.toDataURL('image/png')
|
const url = await this.getUrl(index)
|
||||||
const p = nativeImage.createFromDataURL(png)
|
this.link = 'http://hunlongyu.gitee.io/zy-player-web?url=' + url + '&name=' + this.share.info.name
|
||||||
clipboard.writeImage(p)
|
this.loading = false
|
||||||
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('已复制到剪贴板,快去分享吧~ 严禁传播违法资源!!!')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,20 +1,64 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="listpage" id="star">
|
<div class="listpage" id="star">
|
||||||
<div class="listpage-header" id="star-header">
|
<div class="listpage-header" id="star-header">
|
||||||
<el-switch v-model="viewMode" active-text="海报" active-value="picture" inactive-text="列表" inactive-value="table" @change="updateViewMode"></el-switch>
|
<el-button @click.stop="exportFavoritesEvent" icon="el-icon-upload2" title="导出全部,自动添加扩展名">导出</el-button>
|
||||||
<el-button @click.stop="exportFavoritesEvent" icon="el-icon-upload2">导出</el-button>
|
<el-button @click.stop="importFavoritesEvent" icon="el-icon-download" title="支持同时导入多个文件">导入</el-button>
|
||||||
<el-button @click.stop="importFavoritesEvent" icon="el-icon-download">导入</el-button>
|
<el-button @click.stop="removeSelectedItems" icon="el-icon-delete-solid">{{ multipleSelection.length === 0 ? "清空" : "删除所选" }}</el-button>
|
||||||
<el-button @click.stop="clearFavoritesEvent" icon="el-icon-delete-solid">清空</el-button>
|
<b-button-group>
|
||||||
<el-button @click.stop="updateAllEvent" icon="el-icon-refresh">同步所有收藏</el-button>
|
<el-switch v-model="onlyShowItemsHasUpdate" active-text="有更新" inactive-text="全部" @change="refreshFilteredList"></el-switch>
|
||||||
|
<el-button @click.stop="updateAllEvent" icon="el-icon-refresh">检查更新</el-button>
|
||||||
|
</b-button-group>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="toolbar" v-show="showToolbar">
|
||||||
|
<el-select v-model="selectedAreas" size="small" multiple placeholder="地区" popper-class="popper" :popper-append-to-body="false" @remove-tag="refreshFilteredList" @change="refreshFilteredList">
|
||||||
|
<el-option
|
||||||
|
v-for="item in areas"
|
||||||
|
:key="item"
|
||||||
|
:label="item"
|
||||||
|
:value="item">
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
<el-select v-model="selectedTypes" size="small" multiple placeholder="类型" popper-class="popper" :popper-append-to-body="false" @remove-tag="refreshFilteredList" @change="refreshFilteredList">
|
||||||
|
<el-option
|
||||||
|
v-for="item in types"
|
||||||
|
:key="item"
|
||||||
|
:label="item"
|
||||||
|
:value="item">
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
<el-select v-model="sortKeyword" size="small" placeholder="排序" popper-class="popper" :popper-append-to-body="false" @change="refreshFilteredList">
|
||||||
|
<el-option
|
||||||
|
v-for="item in sortKeywords"
|
||||||
|
:key="item"
|
||||||
|
:label="item"
|
||||||
|
:value="item">
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
<span>
|
||||||
|
上映区间:
|
||||||
|
<el-input-number size="small" v-model="selectedYears.start" :min=0 :max="new Date().getFullYear()" controls-position="right" step-strictly @change="refreshFilteredList"></el-input-number>
|
||||||
|
至
|
||||||
|
<el-input-number size="small" v-model="selectedYears.end" :min=0 :max="new Date().getFullYear()" controls-position="right" step-strictly @change="refreshFilteredList"></el-input-number>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<el-divider class="listpage-header-divider" content-position="right">
|
||||||
|
<el-button type="text" size="mini" @click="toggleViewMode">视图切换</el-button>
|
||||||
|
<el-button type="text" size="mini" @click='() => { showToolbar = !showToolbar; if (!showToolbar) this.refreshFilteredList() }' title="收起工具栏会重置筛选排序">{{ showToolbar ? '隐藏工具栏' : '显示工具栏' }}</el-button>
|
||||||
|
<el-button type="text" size="mini" @click="backTop">回到顶部</el-button>
|
||||||
|
</el-divider>
|
||||||
<div class="listpage-body" id="star-body">
|
<div class="listpage-body" id="star-body">
|
||||||
<div class="show-table" id="star-table" v-show="viewMode === 'table'">
|
<div class="show-table" id="star-table" v-if="setting.starViewMode === 'table'">
|
||||||
<el-table size="mini" fit height="100%" row-key="id"
|
<el-table size="mini" fit height="100%" row-key="id"
|
||||||
ref="starTable"
|
ref="starTable"
|
||||||
:data="list"
|
:data="filteredList"
|
||||||
:cell-class-name="checkUpdate"
|
:cell-class-name="checkUpdate"
|
||||||
@row-click="detailEvent"
|
@row-click="detailEvent"
|
||||||
@sort-change="handleSortChange">
|
@sort-change="handleSortChange"
|
||||||
|
@select="selectionCellClick"
|
||||||
|
@selection-change="handleSelectionChange">
|
||||||
|
<el-table-column
|
||||||
|
type="selection">
|
||||||
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
sortable
|
sortable
|
||||||
:sort-method="(a , b) => sortByLocaleCompare(a.name, b.name)"
|
:sort-method="(a , b) => sortByLocaleCompare(a.name, b.name)"
|
||||||
@@ -78,8 +122,8 @@
|
|||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
</div>
|
</div>
|
||||||
<div class="show-picture" id="star-picture" v-show="viewMode === 'picture'">
|
<div class="show-picture" id="star-picture" v-if="setting.starViewMode === 'picture'">
|
||||||
<Waterfall ref="starWaterfall" :list="list" :gutter="20" :width="240"
|
<Waterfall ref="starWaterfall" :list="filteredList" :gutter="20" :width="240"
|
||||||
:breakpoints="{
|
:breakpoints="{
|
||||||
1200: { //当屏幕宽度小于等于1200
|
1200: { //当屏幕宽度小于等于1200
|
||||||
rowPerView: 4,
|
rowPerView: 4,
|
||||||
@@ -102,7 +146,7 @@
|
|||||||
<div class="update" v-if="props.data.hasUpdate">
|
<div class="update" v-if="props.data.hasUpdate">
|
||||||
<span>有更新</span>
|
<span>有更新</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="progress" v-if="props.data.index && props.data.detail && props.data.detail.m3u8List !== undefined && props.data.detail.m3u8List.length > 1">
|
<div class="progress" v-if="props.data.index && props.data.detail && props.data.detail.fullList[0].list !== undefined && props.data.detail.fullList[0].list.length > 1">
|
||||||
<span>
|
<span>
|
||||||
看至第{{ props.data.index + 1 }}集
|
看至第{{ props.data.index + 1 }}集
|
||||||
</span>
|
</span>
|
||||||
@@ -133,9 +177,9 @@
|
|||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import { mapMutations } from 'vuex'
|
import { mapMutations } from 'vuex'
|
||||||
import { star, sites, setting } from '../lib/dexie'
|
import { history, star, sites, setting } from '../lib/dexie'
|
||||||
import zy from '../lib/site/tools'
|
import zy from '../lib/site/tools'
|
||||||
import { remote } from 'electron'
|
const remote = require('@electron/remote')
|
||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
import Sortable from 'sortablejs'
|
import Sortable from 'sortablejs'
|
||||||
import Waterfall from 'vue-waterfall-plugin'
|
import Waterfall from 'vue-waterfall-plugin'
|
||||||
@@ -146,8 +190,22 @@ export default {
|
|||||||
return {
|
return {
|
||||||
list: [],
|
list: [],
|
||||||
sites: [],
|
sites: [],
|
||||||
viewMode: 'picture',
|
numNoUpdate: 0,
|
||||||
numNoUpdate: 0
|
shiftDown: false,
|
||||||
|
selectionBegin: '',
|
||||||
|
selectionEnd: '',
|
||||||
|
multipleSelection: [],
|
||||||
|
filteredList: [],
|
||||||
|
areas: [],
|
||||||
|
types: [],
|
||||||
|
// Toolbar
|
||||||
|
showToolbar: false,
|
||||||
|
selectedAreas: [],
|
||||||
|
selectedTypes: [],
|
||||||
|
sortKeyword: '',
|
||||||
|
sortKeywords: ['按片名', '按上映年份', '按更新时间'],
|
||||||
|
selectedYears: { start: 0, end: new Date().getFullYear() },
|
||||||
|
onlyShowItemsHasUpdate: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
@@ -185,6 +243,22 @@ export default {
|
|||||||
set (val) {
|
set (val) {
|
||||||
this.SET_SHARE(val)
|
this.SET_SHARE(val)
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
setting: {
|
||||||
|
get () {
|
||||||
|
return this.$store.getters.getSetting
|
||||||
|
},
|
||||||
|
set (val) {
|
||||||
|
this.SET_SETTING(val)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
DetailCache: {
|
||||||
|
get () {
|
||||||
|
return this.$store.getters.getDetailCache
|
||||||
|
},
|
||||||
|
set (val) {
|
||||||
|
this.SET_DetailCache(val)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
@@ -192,9 +266,7 @@ export default {
|
|||||||
if (this.view === 'Star') {
|
if (this.view === 'Star') {
|
||||||
this.getAllsites()
|
this.getAllsites()
|
||||||
this.getFavorites()
|
this.getFavorites()
|
||||||
if (this.$refs.starWaterfall) {
|
if (this.setting.starViewMode === 'table') this.showShiftPrompt()
|
||||||
this.$refs.starWaterfall.refresh()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
numNoUpdate () {
|
numNoUpdate () {
|
||||||
@@ -203,16 +275,110 @@ export default {
|
|||||||
this.numNoUpdate = 0
|
this.numNoUpdate = 0
|
||||||
this.$message.warning('未查询到任何更新')
|
this.$message.warning('未查询到任何更新')
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
list: {
|
||||||
|
handler (list) {
|
||||||
|
this.areas = [...new Set(list.map(ele => ele.detail.area))].filter(x => x)
|
||||||
|
this.types = [...new Set(list.map(ele => ele.detail.type))].filter(x => x)
|
||||||
|
this.refreshFilteredList()
|
||||||
|
},
|
||||||
|
deep: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapMutations(['SET_VIEW', 'SET_DETAIL', 'SET_VIDEO', 'SET_SHARE']),
|
...mapMutations(['SET_VIEW', 'SET_DETAIL', 'SET_VIDEO', 'SET_SHARE', 'SET_SETTING']),
|
||||||
|
toggleViewMode () {
|
||||||
|
this.setting.starViewMode = this.setting.starViewMode === 'picture' ? 'table' : 'picture'
|
||||||
|
if (this.setting.starViewMode === 'table') {
|
||||||
|
setTimeout(() => { this.rowDrop() }, 100)
|
||||||
|
this.showShiftPrompt()
|
||||||
|
} else {
|
||||||
|
setTimeout(() => { if (this.$refs.starWaterfall) this.$refs.starWaterfall.refresh() }, 700)
|
||||||
|
}
|
||||||
|
setting.find().then(res => {
|
||||||
|
res.starViewMode = this.setting.starViewMode
|
||||||
|
setting.update(res)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
backTop () {
|
||||||
|
if (this.setting.starViewMode === 'picture') {
|
||||||
|
document.getElementById('star-body').scrollTop = 0
|
||||||
|
} else {
|
||||||
|
this.$refs.starTable.bodyWrapper.scrollTop = 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
refreshFilteredList () {
|
||||||
|
if (!this.showToolbar) {
|
||||||
|
this.sortKeyword = ''
|
||||||
|
this.selectedAreas = []
|
||||||
|
this.selectedSearchClassNames = []
|
||||||
|
this.selectedYears.start = 0
|
||||||
|
this.selectedYears.end = new Date().getFullYear()
|
||||||
|
this.filteredList = this.list
|
||||||
|
} else {
|
||||||
|
let filteredData = this.list
|
||||||
|
filteredData = filteredData.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))
|
||||||
|
filteredData = filteredData.filter(res => res.detail.year >= this.selectedYears.start)
|
||||||
|
filteredData = filteredData.filter(res => res.detail.year <= this.selectedYears.end)
|
||||||
|
switch (this.sortKeyword) {
|
||||||
|
case '按上映年份':
|
||||||
|
filteredData.sort(function (a, b) {
|
||||||
|
return b.detail.year - a.detail.year
|
||||||
|
})
|
||||||
|
break
|
||||||
|
case '按片名':
|
||||||
|
filteredData.sort(function (a, b) {
|
||||||
|
return a.detail.name.localeCompare(b.detail.name, 'zh')
|
||||||
|
})
|
||||||
|
break
|
||||||
|
case '按更新时间':
|
||||||
|
filteredData.sort(function (a, b) {
|
||||||
|
return new Date(b.detail.last) - new Date(a.detail.last)
|
||||||
|
})
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
this.filteredList = filteredData
|
||||||
|
}
|
||||||
|
if (this.onlyShowItemsHasUpdate) {
|
||||||
|
this.filteredList = this.filteredList.filter(x => x.hasUpdate)
|
||||||
|
}
|
||||||
|
},
|
||||||
handleSortChange (column, prop, order) {
|
handleSortChange (column, prop, order) {
|
||||||
this.updateDatabase()
|
this.updateDatabase()
|
||||||
},
|
},
|
||||||
sortByLocaleCompare (a, b) {
|
sortByLocaleCompare (a, b) {
|
||||||
return a.localeCompare(b, 'zh')
|
return a.localeCompare(b, 'zh')
|
||||||
},
|
},
|
||||||
|
selectionCellClick (selection, row) { // 同history一样,逆序
|
||||||
|
if (this.shiftDown && this.selectionBegin !== '' && selection.includes(row)) {
|
||||||
|
this.selectionEnd = row.id
|
||||||
|
const start = this.list.findIndex(e => e.id === Math.max(this.selectionBegin, this.selectionEnd))
|
||||||
|
const end = this.list.findIndex(e => e.id === Math.min(this.selectionBegin, this.selectionEnd))
|
||||||
|
const selections = this.list.slice(start, end + 1)
|
||||||
|
this.$nextTick(() => {
|
||||||
|
selections.forEach(e => this.$refs.starTable.toggleRowSelection(e, true))
|
||||||
|
})
|
||||||
|
this.selectionBegin = this.selectionEnd = ''
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (selection.includes(row)) {
|
||||||
|
this.selectionBegin = row.id
|
||||||
|
} else {
|
||||||
|
this.selectionBegin = ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleSelectionChange (rows) {
|
||||||
|
this.multipleSelection = rows
|
||||||
|
},
|
||||||
|
removeSelectedItems () {
|
||||||
|
if (!this.multipleSelection.length) this.multipleSelection = this.list
|
||||||
|
this.multipleSelection.forEach(e => star.remove(e.id))
|
||||||
|
this.getFavorites()
|
||||||
|
this.updateDatabase()
|
||||||
|
},
|
||||||
detailEvent (e) {
|
detailEvent (e) {
|
||||||
this.detail = {
|
this.detail = {
|
||||||
show: true,
|
show: true,
|
||||||
@@ -228,9 +394,9 @@ export default {
|
|||||||
},
|
},
|
||||||
async playEvent (e) {
|
async playEvent (e) {
|
||||||
if (e.index) {
|
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 {
|
} 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) {
|
if (e.hasUpdate) {
|
||||||
this.clearHasUpdateFlag(e)
|
this.clearHasUpdateFlag(e)
|
||||||
@@ -249,7 +415,7 @@ export default {
|
|||||||
this.share = {
|
this.share = {
|
||||||
show: true,
|
show: true,
|
||||||
key: e.key,
|
key: e.key,
|
||||||
info: e
|
info: e.detail
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
checkUpdate ({ row, rowIndex }) {
|
checkUpdate ({ row, rowIndex }) {
|
||||||
@@ -265,21 +431,24 @@ export default {
|
|||||||
this.getFavorites()
|
this.getFavorites()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
updateEvent (e) {
|
async updateEvent (e) {
|
||||||
zy.detail(e.key, e.ids).then(detailRes => {
|
try {
|
||||||
var doc = {
|
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,
|
id: e.id,
|
||||||
key: e.key,
|
key: e.key,
|
||||||
ids: e.ids,
|
ids: e.ids,
|
||||||
site: e.site,
|
site: e.site,
|
||||||
name: e.name,
|
name: e.name,
|
||||||
detail: detailRes,
|
detail: this.DetailCache[e.key + '@' + e.ids],
|
||||||
index: e.index
|
index: e.index
|
||||||
}
|
}
|
||||||
star.get(e.id).then(resStar => {
|
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
|
doc.hasUpdate = true
|
||||||
var msg = `同步"${e.name}"成功, 检查到更新。`
|
const msg = `检查到"${e.name}"有更新。`
|
||||||
this.$message.success(msg)
|
this.$message.success(msg)
|
||||||
} else {
|
} else {
|
||||||
this.numNoUpdate += 1
|
this.numNoUpdate += 1
|
||||||
@@ -287,10 +456,10 @@ export default {
|
|||||||
star.update(e.id, doc)
|
star.update(e.id, doc)
|
||||||
this.getFavorites()
|
this.getFavorites()
|
||||||
})
|
})
|
||||||
}).catch(err => {
|
} catch (err) {
|
||||||
var msg = `同步"${e.name}"失败, 请重试。`
|
const msg = `更新"${e.name}"失败, 请重试。`
|
||||||
this.$message.warning(msg, err)
|
this.$message.warning(msg, err)
|
||||||
})
|
}
|
||||||
},
|
},
|
||||||
updateAllEvent () {
|
updateAllEvent () {
|
||||||
this.numNoUpdate = 0
|
this.numNoUpdate = 0
|
||||||
@@ -298,41 +467,22 @@ export default {
|
|||||||
this.updateEvent(e)
|
this.updateEvent(e)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
downloadEvent (e) {
|
async downloadEvent (e) {
|
||||||
zy.download(e.key, e.ids).then(res => {
|
const db = await history.find({ site: e.key, ids: e.ids })
|
||||||
if (res) {
|
let videoFlag
|
||||||
const text = res.m3u8List
|
if (db) videoFlag = db.videoFlag
|
||||||
if (text) {
|
zy.download(e.key, e.ids, videoFlag).then(res => {
|
||||||
const list = text.split('#')
|
clipboard.writeText(res.downloadUrls)
|
||||||
let downloadUrl = ''
|
this.$message.success(res.info)
|
||||||
for (const i of list) {
|
}).catch((err) => {
|
||||||
const url = encodeURI(i.split('$')[1])
|
this.$message.error(err.info)
|
||||||
downloadUrl += (url + '\n')
|
|
||||||
}
|
|
||||||
clipboard.writeText(downloadUrl)
|
|
||||||
this.$message.success('『MP4』格式的链接已复制, 快去下载吧!')
|
|
||||||
} else {
|
|
||||||
this.$message.warning('没有查询到下载链接.')
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
zy.detail(e.key, e.ids).then(res => {
|
|
||||||
const list = [...res.m3u8List]
|
|
||||||
let downloadUrl = ''
|
|
||||||
for (const i of list) {
|
|
||||||
const url = encodeURI(i.split('$')[1])
|
|
||||||
downloadUrl += (url + '\n')
|
|
||||||
}
|
|
||||||
clipboard.writeText(downloadUrl)
|
|
||||||
this.$message.success('『M3U8』格式的链接已复制, 快去下载吧!')
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
getSiteName (row) {
|
getSiteName (row) {
|
||||||
if (row.site) {
|
if (row.site) {
|
||||||
return row.site.name
|
return row.site.name
|
||||||
} else {
|
} else {
|
||||||
var site = this.sites.find(e => e.key === row.key)
|
const site = this.sites.find(e => e.key === row.key)
|
||||||
if (site) {
|
if (site) {
|
||||||
return site.name
|
return site.name
|
||||||
}
|
}
|
||||||
@@ -362,13 +512,12 @@ export default {
|
|||||||
const str = JSON.stringify(arr, null, 2)
|
const str = JSON.stringify(arr, null, 2)
|
||||||
const options = {
|
const options = {
|
||||||
filters: [
|
filters: [
|
||||||
{ name: 'JSON file', extensions: ['json'] },
|
{ name: 'JSON file', extensions: ['json'] }
|
||||||
{ name: 'Normal text file', extensions: ['txt'] },
|
|
||||||
{ name: 'All types', extensions: ['*'] }
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
remote.dialog.showSaveDialog(options).then(result => {
|
remote.dialog.showSaveDialog(options).then(result => {
|
||||||
if (!result.canceled) {
|
if (!result.canceled) {
|
||||||
|
if (!result.filePath.endsWith('.json')) result.filePath += '.json'
|
||||||
fs.writeFileSync(result.filePath, str)
|
fs.writeFileSync(result.filePath, str)
|
||||||
this.$message.success('导出收藏成功')
|
this.$message.success('导出收藏成功')
|
||||||
}
|
}
|
||||||
@@ -379,23 +528,31 @@ export default {
|
|||||||
importFavoritesEvent () {
|
importFavoritesEvent () {
|
||||||
const options = {
|
const options = {
|
||||||
filters: [
|
filters: [
|
||||||
{ name: 'JSON file', extensions: ['json'] },
|
{ name: 'JSON file', extensions: ['json'] }
|
||||||
{ name: 'Normal text file', extensions: ['txt'] },
|
|
||||||
{ name: 'All types', extensions: ['*'] }
|
|
||||||
],
|
],
|
||||||
properties: ['openFile', 'multiSelections']
|
properties: ['openFile', 'multiSelections']
|
||||||
}
|
}
|
||||||
remote.dialog.showOpenDialog(options).then(result => {
|
remote.dialog.showOpenDialog(options).then(result => {
|
||||||
if (!result.canceled) {
|
if (!result.canceled) {
|
||||||
var starList = Array.from(this.list)
|
const starList = Array.from(this.list)
|
||||||
var id = this.list.length + 1
|
let id = this.list.length + 1
|
||||||
result.filePaths.forEach(file => {
|
result.filePaths.forEach(file => {
|
||||||
var str = fs.readFileSync(file)
|
const str = fs.readFileSync(file)
|
||||||
const json = JSON.parse(str)
|
const json = JSON.parse(str)
|
||||||
json.reverse().forEach(ele => {
|
json.reverse().forEach(ele => {
|
||||||
const starExists = starList.some(x => x.key === ele.key && x.ids === ele.ids)
|
const starExists = starList.some(x => x.key === ele.key && x.ids === ele.ids)
|
||||||
if (!starExists) {
|
if (!starExists) {
|
||||||
var doc = {
|
const newDetail = {
|
||||||
|
director: ele.director,
|
||||||
|
actor: ele.actor,
|
||||||
|
type: ele.type,
|
||||||
|
area: ele.area,
|
||||||
|
lang: ele.lang,
|
||||||
|
year: ele.year,
|
||||||
|
last: ele.last,
|
||||||
|
note: ele.note
|
||||||
|
}
|
||||||
|
const doc = {
|
||||||
id: id,
|
id: id,
|
||||||
key: ele.key,
|
key: ele.key,
|
||||||
ids: ele.ids,
|
ids: ele.ids,
|
||||||
@@ -404,16 +561,7 @@ export default {
|
|||||||
hasUpdate: ele.hasUpdate,
|
hasUpdate: ele.hasUpdate,
|
||||||
index: ele.index,
|
index: ele.index,
|
||||||
rate: ele.rate,
|
rate: ele.rate,
|
||||||
detail: ele.detail === undefined ? {
|
detail: ele.detail === undefined ? newDetail : ele.detail
|
||||||
director: ele.director,
|
|
||||||
actor: ele.actor,
|
|
||||||
type: ele.type,
|
|
||||||
area: ele.area,
|
|
||||||
lang: ele.lang,
|
|
||||||
year: ele.year,
|
|
||||||
last: ele.last,
|
|
||||||
note: ele.note
|
|
||||||
} : ele.detail
|
|
||||||
}
|
}
|
||||||
id += 1
|
id += 1
|
||||||
starList.push(doc)
|
starList.push(doc)
|
||||||
@@ -429,11 +577,6 @@ export default {
|
|||||||
this.$message.error(err)
|
this.$message.error(err)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
clearFavoritesEvent () {
|
|
||||||
star.clear().then(e => {
|
|
||||||
this.getFavorites()
|
|
||||||
})
|
|
||||||
},
|
|
||||||
syncTableData () {
|
syncTableData () {
|
||||||
if (this.$refs.starTable.tableData && this.$refs.starTable.tableData.length === this.list.length) {
|
if (this.$refs.starTable.tableData && this.$refs.starTable.tableData.length === this.list.length) {
|
||||||
this.list = this.$refs.starTable.tableData
|
this.list = this.$refs.starTable.tableData
|
||||||
@@ -442,7 +585,7 @@ export default {
|
|||||||
updateDatabase () {
|
updateDatabase () {
|
||||||
this.syncTableData()
|
this.syncTableData()
|
||||||
star.clear().then(res => {
|
star.clear().then(res => {
|
||||||
var id = this.list.length
|
let id = this.list.length
|
||||||
this.list.forEach(ele => {
|
this.list.forEach(ele => {
|
||||||
ele.id = id
|
ele.id = id
|
||||||
id -= 1
|
id -= 1
|
||||||
@@ -451,6 +594,7 @@ export default {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
rowDrop () {
|
rowDrop () {
|
||||||
|
if (!document.getElementById('star-table')) return
|
||||||
const tbody = document.getElementById('star-table').querySelector('.el-table__body-wrapper tbody')
|
const tbody = document.getElementById('star-table').querySelector('.el-table__body-wrapper tbody')
|
||||||
const _this = this
|
const _this = this
|
||||||
Sortable.create(tbody, {
|
Sortable.create(tbody, {
|
||||||
@@ -461,33 +605,28 @@ export default {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
getViewMode () {
|
showShiftPrompt () {
|
||||||
setting.find().then(res => {
|
if (this.setting.shiftTooltipLimitTimes === undefined) this.setting.shiftTooltipLimitTimes = 5
|
||||||
this.viewMode = res.starViewMode
|
if (this.setting.shiftTooltipLimitTimes) {
|
||||||
})
|
this.$message.info('多选时支持shift快捷键')
|
||||||
},
|
this.setting.shiftTooltipLimitTimes--
|
||||||
updateViewMode () {
|
setting.find().then(res => {
|
||||||
setting.find().then(res => {
|
res.shiftTooltipLimitTimes = this.setting.shiftTooltipLimitTimes
|
||||||
res.starViewMode = this.viewMode
|
setting.update(res)
|
||||||
setting.update(res)
|
})
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created () {
|
created () {
|
||||||
this.getFavorites()
|
this.getFavorites()
|
||||||
this.getViewMode()
|
|
||||||
},
|
},
|
||||||
mounted () {
|
mounted () {
|
||||||
this.rowDrop()
|
if (this.setting.starViewMode === 'table') setTimeout(() => { this.rowDrop() }, 100)
|
||||||
window.addEventListener('resize', () => {
|
addEventListener('keydown', code => { if (code.keyCode === 16) this.shiftDown = true })
|
||||||
if (this.$refs.starWaterfall && this.view === 'Star') {
|
addEventListener('keyup', code => { if (code.keyCode === 16) this.shiftDown = false })
|
||||||
this.$refs.starWaterfall.resize()
|
addEventListener('resize', () => {
|
||||||
this.$refs.starWaterfall.refresh()
|
setTimeout(() => { if (this.$refs.starWaterfall) this.$refs.starWaterfall.resize() }, 500)
|
||||||
setTimeout(() => {
|
})
|
||||||
this.$refs.starWaterfall.refresh()
|
|
||||||
}, 500)
|
|
||||||
}
|
|
||||||
}, false)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import Share from './Share'
|
|||||||
import History from './History'
|
import History from './History'
|
||||||
import EditSites from './EditSites'
|
import EditSites from './EditSites'
|
||||||
import IPTV from './IPTV'
|
import IPTV from './IPTV'
|
||||||
import Recommendation from './Recommendation'
|
// import Recommendation from './Recommendation'
|
||||||
export default {
|
export default {
|
||||||
registerComponents () {
|
registerComponents () {
|
||||||
Vue.component('Aside', Aside)
|
Vue.component('Aside', Aside)
|
||||||
@@ -24,6 +24,6 @@ export default {
|
|||||||
Vue.component('History', History)
|
Vue.component('History', History)
|
||||||
Vue.component('EditSites', EditSites)
|
Vue.component('EditSites', EditSites)
|
||||||
Vue.component('IPTV', IPTV)
|
Vue.component('IPTV', IPTV)
|
||||||
Vue.component('Recommendation', Recommendation)
|
// Vue.component('Recommendation', Recommendation)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
28
src/lib/dexie/cachedMovies.js
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import db from './dexie'
|
||||||
|
const { cachedMovies } = db
|
||||||
|
export default {
|
||||||
|
async add (doc) {
|
||||||
|
return await cachedMovies.add(doc)
|
||||||
|
},
|
||||||
|
async bulkAdd (doc) {
|
||||||
|
return await cachedMovies.bulkAdd(doc)
|
||||||
|
},
|
||||||
|
async find (doc) {
|
||||||
|
return await cachedMovies.where(doc).first()
|
||||||
|
},
|
||||||
|
async update (id, docs) {
|
||||||
|
return await cachedMovies.update(id, docs)
|
||||||
|
},
|
||||||
|
async all () {
|
||||||
|
return await cachedMovies.toArray()
|
||||||
|
},
|
||||||
|
async remove (id) {
|
||||||
|
return await cachedMovies.delete(id)
|
||||||
|
},
|
||||||
|
async get (id) {
|
||||||
|
return await cachedMovies.get(id)
|
||||||
|
},
|
||||||
|
async clear () {
|
||||||
|
return await cachedMovies.clear()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,7 @@
|
|||||||
import Dexie from 'dexie'
|
import Dexie from 'dexie'
|
||||||
import { setting, sites, localKey, iptv, recommendations } from './initData'
|
import { sites, localKey, iptv, recommendations, iniSetting } from './initData'
|
||||||
|
|
||||||
const db = new Dexie('zy')
|
const db = new Dexie('zy')
|
||||||
|
|
||||||
db.version(4).stores({
|
db.version(4).stores({
|
||||||
search: '++id, keywords',
|
search: '++id, keywords',
|
||||||
setting: 'id, theme, site, shortcut, view, volume, externalPlayer, searchGroup, excludeRootClasses, excludeR18Films, forwardTimeInSec, starViewMode, recommandationViewMode, searchViewMode, password, proxy, allowPassWhenIptvCheck, autocleanWhenIptvCheck',
|
setting: 'id, theme, site, shortcut, view, volume, externalPlayer, searchGroup, excludeRootClasses, excludeR18Films, forwardTimeInSec, starViewMode, recommandationViewMode, searchViewMode, password, proxy, allowPassWhenIptvCheck, autocleanWhenIptvCheck',
|
||||||
@@ -16,8 +15,102 @@ db.version(4).stores({
|
|||||||
channelList: '++id, name, prefer, channels, group, isActive'
|
channelList: '++id, name, prefer, channels, group, isActive'
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 开发和稳定版同一版本号会有不同的数据库
|
||||||
|
// 参考https://github.com/dfahlander/Dexie.js/releases/tag/v3.0.0-alpha.3 upgrade可以改变主键和表名了
|
||||||
|
// https://dexie.org/docs/Version/Version.stores()
|
||||||
|
// https://dexie.org/docs/Version/Version.upgrade()
|
||||||
|
// https://ahuigo.github.io/b/ria/js-indexedDB#/ 比较旧,适当参考
|
||||||
|
db.version(5).stores({
|
||||||
|
shortcut: null
|
||||||
|
})
|
||||||
|
|
||||||
|
db.version(6).stores({
|
||||||
|
shortcut: '++id, name, key, desc'
|
||||||
|
}).upgrade(async tx => {
|
||||||
|
await tx.shortcut.bulkAdd(localKey)
|
||||||
|
})
|
||||||
|
|
||||||
|
db.version(7).stores({
|
||||||
|
sites: '++id, key, name, api, download, jiexiUrl, isActive, group',
|
||||||
|
history: '++id, [site+ids], name, type, year, index, time, duration, detail, onlinePlay'
|
||||||
|
}).upgrade(trans => {
|
||||||
|
trans.sites.toCollection().modify(site => {
|
||||||
|
site.jiexiUrl = ''
|
||||||
|
})
|
||||||
|
trans.history.toCollection().modify(record => {
|
||||||
|
record.detail.fullList = [].concat(record.detail.m3u8List)
|
||||||
|
delete record.detail.m3u8List
|
||||||
|
})
|
||||||
|
trans.star.toCollection().modify(favorite => {
|
||||||
|
favorite.detail.fullList = [].concat(favorite.detail.m3u8List)
|
||||||
|
delete favorite.detail.m3u8List
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
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.version(9).stores({
|
||||||
|
history: '++id, [site+ids], name, type, year, index, time, duration, detail, onlinePlay, hasUpdate'
|
||||||
|
})
|
||||||
|
|
||||||
|
db.version(10).stores({
|
||||||
|
setting: 'id, theme, shortcut, view, volume, externalPlayer, searchGroup, excludeRootClasses, excludeR18Films, forwardTimeInSec, starViewMode, recommandationViewMode, searchViewMode, password, proxy, allowPassWhenIptvCheck, autocleanWhenIptvCheck, rootClassFilter, r18ClassFilter, classFilter'
|
||||||
|
}).upgrade(trans => {
|
||||||
|
trans.setting.toCollection().modify(setting => {
|
||||||
|
delete setting.site
|
||||||
|
setting.rootClassFilter = ['电影', '电影片', '电视剧', '连续剧', '综艺', '动漫']
|
||||||
|
setting.r18ClassFilter = ['伦理', '论理', '倫理', '福利', '激情', '理论', '写真', '情色', '美女', '街拍', '赤足', '性感', '里番', 'VIP']
|
||||||
|
setting.classFilter = ['电影', '电影片', '电视剧', '连续剧', '综艺', '动漫', '伦理', '论理', '倫理', '福利', '激情', '理论', '写真', '情色', '美女', '街拍', '赤足', '性感', '里番', 'VIP']
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
db.version(11).stores({
|
||||||
|
setting: 'id, theme, shortcut, view, volume, externalPlayer, searchGroup, excludeRootClasses, excludeR18Films, forwardTimeInSec, starViewMode, recommandationViewMode,' +
|
||||||
|
'searchViewMode, password, proxy, allowPassWhenIptvCheck, autocleanWhenIptvCheck, rootClassFilter, r18ClassFilter, classFilter, restoreWindowPositionAndSize, windowPositionAndSize, pauseWhenMinimize',
|
||||||
|
cachedMovies: '++id, [key+ids], site, name, detail, index, rate, hasUpdate'
|
||||||
|
}).upgrade(trans => {
|
||||||
|
trans.setting.toCollection().modify(setting => {
|
||||||
|
setting.restoreWindowPositionAndSize = false
|
||||||
|
setting.windowPositionAndSize = {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
width: 1080,
|
||||||
|
height: 720
|
||||||
|
}
|
||||||
|
setting.pauseWhenMinimize = false
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
db.version(11).stores({
|
||||||
|
setting: 'id, theme, shortcut, view, volume, externalPlayer, searchGroup, excludeRootClasses, excludeR18Films, forwardTimeInSec, starViewMode, recommandationViewMode,' +
|
||||||
|
'searchViewMode, password, proxy, allowPassWhenIptvCheck, autocleanWhenIptvCheck, rootClassFilter, r18ClassFilter, classFilter, restoreWindowPositionAndSize,' +
|
||||||
|
'windowPositionAndSize, pauseWhenMinimize, sitesDataURL, defaultParseURL'
|
||||||
|
}).upgrade(trans => {
|
||||||
|
trans.setting.toCollection().modify(setting => {
|
||||||
|
setting.sitesDataURL = 'https://raw.iqiq.io/Hunlongyu/ZY-Player-Resources/main/Sites/20220713.json'
|
||||||
|
setting.defaultParseURL = 'https://jx.bpba.cc/?v='
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
db.version(12).stores({
|
||||||
|
sites: '++id, key, name, api, download, jiexiUrl, isActive, group, reverseOrder'
|
||||||
|
}).upgrade(trans => {
|
||||||
|
trans.sites.toCollection().modify(site => {
|
||||||
|
site.reverseOrder = false
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
db.on('populate', () => {
|
db.on('populate', () => {
|
||||||
db.setting.bulkAdd(setting)
|
db.setting.bulkAdd(iniSetting)
|
||||||
db.sites.bulkAdd(sites)
|
db.sites.bulkAdd(sites)
|
||||||
db.shortcut.bulkAdd(localKey)
|
db.shortcut.bulkAdd(localKey)
|
||||||
db.iptv.bulkAdd(iptv)
|
db.iptv.bulkAdd(iptv)
|
||||||
|
|||||||
@@ -19,6 +19,9 @@ export default {
|
|||||||
async remove (id) {
|
async remove (id) {
|
||||||
return await history.delete(id)
|
return await history.delete(id)
|
||||||
},
|
},
|
||||||
|
async get (id) {
|
||||||
|
return await history.get(id)
|
||||||
|
},
|
||||||
async clear () {
|
async clear () {
|
||||||
return await history.clear()
|
return await history.clear()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import search from './search'
|
|||||||
import iptv from './iptv'
|
import iptv from './iptv'
|
||||||
import channelList from './channelList'
|
import channelList from './channelList'
|
||||||
import recommendation from './recommendation'
|
import recommendation from './recommendation'
|
||||||
|
import cachedMovies from './cachedMovies'
|
||||||
|
|
||||||
export {
|
export {
|
||||||
history,
|
history,
|
||||||
@@ -19,5 +20,6 @@ export {
|
|||||||
iptv,
|
iptv,
|
||||||
channelList,
|
channelList,
|
||||||
search,
|
search,
|
||||||
recommendation
|
recommendation,
|
||||||
|
cachedMovies
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,259 +1 @@
|
|||||||
[
|
[]
|
||||||
{
|
|
||||||
"id": 1,
|
|
||||||
"key": "mahuazy",
|
|
||||||
"name": "麻花资源",
|
|
||||||
"api": "http://www.mhapi123.com/inc/ldg_api.php",
|
|
||||||
"download": "",
|
|
||||||
"group": "默认",
|
|
||||||
"isActive": true,
|
|
||||||
"status": "可用"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 2,
|
|
||||||
"key": "1886zy",
|
|
||||||
"name": "1886 资源",
|
|
||||||
"api": "http://cj.1886zy.co/inc/api.php",
|
|
||||||
"download": "",
|
|
||||||
"group": "默认",
|
|
||||||
"isActive": true,
|
|
||||||
"status": "可用"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "wolongzy",
|
|
||||||
"id": 3,
|
|
||||||
"name": "卧龙资源网",
|
|
||||||
"api": "https://www.mhapi123.com/inc/api.php",
|
|
||||||
"download": "",
|
|
||||||
"group": "默认",
|
|
||||||
"isActive": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 4,
|
|
||||||
"key": "123ku",
|
|
||||||
"name": "123 资源",
|
|
||||||
"api": "http://cj.123ku2.com:12315/inc/api.php",
|
|
||||||
"download": "",
|
|
||||||
"group": "默认",
|
|
||||||
"isActive": true,
|
|
||||||
"status": "可用"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 5,
|
|
||||||
"key": "subo988",
|
|
||||||
"name": "速播资源站",
|
|
||||||
"api": "https://www.subo988.com/inc/api.php",
|
|
||||||
"download": "",
|
|
||||||
"group": "默认",
|
|
||||||
"isActive": true,
|
|
||||||
"status": "可用"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 6,
|
|
||||||
"key": "88zyw",
|
|
||||||
"name": "88 影视资源站",
|
|
||||||
"api": "http://www.88zyw.net/inc/api.php",
|
|
||||||
"download": "",
|
|
||||||
"group": "默认",
|
|
||||||
"isActive": true,
|
|
||||||
"status": "可用"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "zuidazy",
|
|
||||||
"id": 7,
|
|
||||||
"name": "最大资源网",
|
|
||||||
"api": "http://www.zdziyuan.com/inc/ldg_sea.php",
|
|
||||||
"download": "http://www.zdziyuan.com/inc/apidown.php",
|
|
||||||
"group": "默认",
|
|
||||||
"isActive": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "mbo",
|
|
||||||
"id": 8,
|
|
||||||
"name": "秒播资源",
|
|
||||||
"api": "http://caiji.mb77.vip/inc/seacmsapi.php",
|
|
||||||
"download": "",
|
|
||||||
"group": "默认",
|
|
||||||
"isActive": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 9,
|
|
||||||
"key": "apibdzy",
|
|
||||||
"name": "百度云资源",
|
|
||||||
"api": "https://api.apibdzy.com/api.php/provide/vod/at/xml",
|
|
||||||
"download": "",
|
|
||||||
"group": "默认",
|
|
||||||
"isActive": true,
|
|
||||||
"status": "可用"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 10,
|
|
||||||
"key": "okzy",
|
|
||||||
"name": "OK 资源网",
|
|
||||||
"api": "http://cj.okzy.tv/inc/api.php",
|
|
||||||
"download": "http://cj.okzy.tv/inc/apidown.php",
|
|
||||||
"group": "默认",
|
|
||||||
"isActive": true,
|
|
||||||
"status": "可用"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 11,
|
|
||||||
"key": "kuyunzy",
|
|
||||||
"name": "酷云资源",
|
|
||||||
"api": "http://caiji.kuyun98.com/inc/ldg_api.php",
|
|
||||||
"download": "http://caiji.kuyun98.com/inc/apidown.php",
|
|
||||||
"group": "默认",
|
|
||||||
"isActive": true,
|
|
||||||
"status": "可用"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 12,
|
|
||||||
"key": "kubozy",
|
|
||||||
"name": "酷播资源",
|
|
||||||
"api": "http://api.kbzyapi.com/inc/api.php",
|
|
||||||
"download": "",
|
|
||||||
"group": "默认",
|
|
||||||
"isActive": true,
|
|
||||||
"status": "可用"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 13,
|
|
||||||
"key": "yongjiuzy",
|
|
||||||
"name": "永久资源",
|
|
||||||
"api": "http://cj.yongjiuzyw.com/inc/api.php",
|
|
||||||
"download": "",
|
|
||||||
"group": "默认",
|
|
||||||
"isActive": true,
|
|
||||||
"status": "可用"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 14,
|
|
||||||
"key": "rrzy",
|
|
||||||
"name": "人人资源",
|
|
||||||
"api": "https://www.rrzyw.cc/api.php/provide/vod/from/rrm3u8/at/xml/",
|
|
||||||
"download": "",
|
|
||||||
"group": "默认",
|
|
||||||
"isActive": true,
|
|
||||||
"status": "可用"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 15,
|
|
||||||
"key": "bbkdj",
|
|
||||||
"name": "步步高顶尖资源网",
|
|
||||||
"api": "http://api.bbkdj.com/api",
|
|
||||||
"download": "",
|
|
||||||
"group": "默认",
|
|
||||||
"isActive": true,
|
|
||||||
"status": "可用"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 16,
|
|
||||||
"key": "solezy",
|
|
||||||
"name": "搜乐资源网",
|
|
||||||
"api": "https://www.caijizy.vip/api.php/provide/vod/at/xml/",
|
|
||||||
"download": "",
|
|
||||||
"group": "默认",
|
|
||||||
"isActive": true,
|
|
||||||
"status": "可用"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 17,
|
|
||||||
"key": "zuixinzy",
|
|
||||||
"name": "最新资源",
|
|
||||||
"api": "http://api.zuixinapi.com/inc/api.php",
|
|
||||||
"download": "",
|
|
||||||
"group": "默认",
|
|
||||||
"isActive": true,
|
|
||||||
"status": "可用"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 18,
|
|
||||||
"key": "605zy",
|
|
||||||
"name": "605资源",
|
|
||||||
"api": "http://www.605zy.net/inc/seacmsapi.php",
|
|
||||||
"download": "",
|
|
||||||
"group": "默认",
|
|
||||||
"isActive": true,
|
|
||||||
"status": "可用"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 19,
|
|
||||||
"key": "doubanzy",
|
|
||||||
"name": "豆瓣电影资源",
|
|
||||||
"api": "http://v.1988cj.com/inc/api.php",
|
|
||||||
"download": "http://v.1988cj.com/inc/apidown.php",
|
|
||||||
"group": "默认",
|
|
||||||
"isActive": true,
|
|
||||||
"status": "可用"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 20,
|
|
||||||
"key": "135zy",
|
|
||||||
"name": "135 资源网",
|
|
||||||
"api": "http://cj.zycjw1.com/inc/api.php",
|
|
||||||
"download": "http://cj.zycjw1.com/inc/apidown.php",
|
|
||||||
"group": "默认",
|
|
||||||
"isActive": true,
|
|
||||||
"status": "可用"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 21,
|
|
||||||
"key": "mgtvzy",
|
|
||||||
"name": "芒果 TV 资源网",
|
|
||||||
"api": "https://api.shijiapi.com/api.php/provide/vod/at/xml/",
|
|
||||||
"download": "",
|
|
||||||
"group": "默认",
|
|
||||||
"isActive": true,
|
|
||||||
"status": "可用"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 22,
|
|
||||||
"key": "209zy",
|
|
||||||
"name": "209 资源",
|
|
||||||
"api": "http://cj.1156zy.com/inc/api.php",
|
|
||||||
"download": "",
|
|
||||||
"group": "默认",
|
|
||||||
"isActive": true,
|
|
||||||
"status": "可用"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 23,
|
|
||||||
"key": "kkzy",
|
|
||||||
"name": "快快资源",
|
|
||||||
"api": "https://api.kkzy.tv/inc/api.php",
|
|
||||||
"download": "",
|
|
||||||
"group": "默认",
|
|
||||||
"isActive": true,
|
|
||||||
"status": "可用"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 24,
|
|
||||||
"key": "mokazy",
|
|
||||||
"name": "魔卡资源网",
|
|
||||||
"api": "https://cj.heiyap.com/api.php/provide/vod/at/xml/",
|
|
||||||
"download": "",
|
|
||||||
"group": "默认",
|
|
||||||
"isActive": true,
|
|
||||||
"status": "可用"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 25,
|
|
||||||
"key": "158zy",
|
|
||||||
"name": "壹伍捌资源网",
|
|
||||||
"api": "http://cj.158zyz.net:158/inc/api.php",
|
|
||||||
"download": "",
|
|
||||||
"group": "默认",
|
|
||||||
"isActive": true,
|
|
||||||
"status": "可用"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 26,
|
|
||||||
"key": "kyzy",
|
|
||||||
"name": "快影资源站",
|
|
||||||
"api": "https://www.kyzy.tv/api.php/kyyun/vod/at/xml/",
|
|
||||||
"download": "",
|
|
||||||
"group": "默认",
|
|
||||||
"isActive": true,
|
|
||||||
"status": "可用"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|||||||
82
src/lib/dexie/iniData/iniSetting.json
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
[{
|
||||||
|
"id": 0,
|
||||||
|
"theme": "light",
|
||||||
|
"shortcut": true,
|
||||||
|
"view": "picture",
|
||||||
|
"externalPlayer": "",
|
||||||
|
"searchGroup": "全站",
|
||||||
|
"excludeRootClasses": true,
|
||||||
|
"excludeR18Films": true,
|
||||||
|
"forwardTimeInSec": 5,
|
||||||
|
"waitingTimeInSec": 15,
|
||||||
|
"starViewMode": "picture",
|
||||||
|
"recommendationViewMode": "picture",
|
||||||
|
"historyViewMode": "picture",
|
||||||
|
"searchViewMode": "picture",
|
||||||
|
"password": "",
|
||||||
|
"proxy": {
|
||||||
|
"type": "none",
|
||||||
|
"scheme": "",
|
||||||
|
"url": "",
|
||||||
|
"port": ""
|
||||||
|
},
|
||||||
|
"allowPassWhenIptvCheck": true,
|
||||||
|
"autocleanWhenIptvCheck": false,
|
||||||
|
"autoChangeSourceWhenIptvStalling": true,
|
||||||
|
"shortcutModified": false,
|
||||||
|
"sitesDataURL": "https://raw.iqiq.io/Hunlongyu/ZY-Player-Resources/main/Sites/20220713.json",
|
||||||
|
"rootClassFilter": [
|
||||||
|
"电影",
|
||||||
|
"电影片",
|
||||||
|
"电视剧",
|
||||||
|
"连续剧",
|
||||||
|
"综艺",
|
||||||
|
"动漫"
|
||||||
|
],
|
||||||
|
"r18ClassFilter": [
|
||||||
|
"伦理",
|
||||||
|
"论理",
|
||||||
|
"倫理",
|
||||||
|
"福利",
|
||||||
|
"激情",
|
||||||
|
"理论",
|
||||||
|
"写真",
|
||||||
|
"情色",
|
||||||
|
"美女",
|
||||||
|
"街拍",
|
||||||
|
"赤足",
|
||||||
|
"性感",
|
||||||
|
"里番",
|
||||||
|
"VIP"
|
||||||
|
],
|
||||||
|
"classFilter": [
|
||||||
|
"电影",
|
||||||
|
"电影片",
|
||||||
|
"电视剧",
|
||||||
|
"连续剧",
|
||||||
|
"综艺",
|
||||||
|
"动漫",
|
||||||
|
"伦理",
|
||||||
|
"论理",
|
||||||
|
"倫理",
|
||||||
|
"福利",
|
||||||
|
"激情",
|
||||||
|
"理论",
|
||||||
|
"写真",
|
||||||
|
"情色",
|
||||||
|
"美女",
|
||||||
|
"街拍",
|
||||||
|
"赤足",
|
||||||
|
"性感",
|
||||||
|
"里番",
|
||||||
|
"VIP"
|
||||||
|
],
|
||||||
|
"restoreWindowPositionAndSize": false,
|
||||||
|
"windowPositionAndSize": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"width": 1080,
|
||||||
|
"height": 720
|
||||||
|
},
|
||||||
|
"pauseWhenMinimize": false
|
||||||
|
}]
|
||||||
112
src/lib/dexie/iniData/localKey.json
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "playAndPause",
|
||||||
|
"desc": "播放或暂停",
|
||||||
|
"key": "space"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "forward",
|
||||||
|
"desc": "快进",
|
||||||
|
"key": "right"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "back",
|
||||||
|
"desc": "快退",
|
||||||
|
"key": "left"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "volumeUp",
|
||||||
|
"desc": "音量调高",
|
||||||
|
"key": "up"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "volumeDown",
|
||||||
|
"desc": "音量调低",
|
||||||
|
"key": "down"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "mute",
|
||||||
|
"desc": "静音",
|
||||||
|
"key": "m"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "top",
|
||||||
|
"desc": "置顶或退出置顶",
|
||||||
|
"key": "t"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "fullscreen",
|
||||||
|
"desc": "进入或退出全屏",
|
||||||
|
"key": "f"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "escape",
|
||||||
|
"desc": "退出全屏/精简模式",
|
||||||
|
"key": "esc"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "next",
|
||||||
|
"desc": "下一集",
|
||||||
|
"key": "alt+right"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "prev",
|
||||||
|
"desc": "上一集",
|
||||||
|
"key": "alt+left"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "home",
|
||||||
|
"desc": "跳到视频开始位置",
|
||||||
|
"key": "home"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "end",
|
||||||
|
"desc": "跳到视频结束位置",
|
||||||
|
"key": "end"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "startPosition",
|
||||||
|
"desc": "标记片头",
|
||||||
|
"key": "ctrl+home"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "endPosition",
|
||||||
|
"desc": "标记片尾",
|
||||||
|
"key": "ctrl+end"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "clearPosition",
|
||||||
|
"desc": "清除标记",
|
||||||
|
"key": "ctrl+del"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "opacityUp",
|
||||||
|
"desc": "透明度调高",
|
||||||
|
"key": "alt+up"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "opacityDown",
|
||||||
|
"desc": "透明度调低",
|
||||||
|
"key": "alt+down"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "playbackRateUp",
|
||||||
|
"desc": "播放倍速加快",
|
||||||
|
"key": "pageup"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "playbackRateDown",
|
||||||
|
"desc": "播放倍速减慢",
|
||||||
|
"key": "pagedown"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "mini",
|
||||||
|
"desc": "进入或退出mini模式",
|
||||||
|
"key": "alt+m"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "resetMini",
|
||||||
|
"desc": "重置mini窗口",
|
||||||
|
"key": "ctrl+0"
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -1,128 +1,3 @@
|
|||||||
const setting = [
|
|
||||||
{
|
|
||||||
id: 0,
|
|
||||||
theme: 'light',
|
|
||||||
shortcut: true,
|
|
||||||
view: 'picture',
|
|
||||||
externalPlayer: '',
|
|
||||||
searchGroup: '全站',
|
|
||||||
excludeRootClasses: true,
|
|
||||||
excludeR18Films: true,
|
|
||||||
forwardTimeInSec: 5,
|
|
||||||
starViewMode: 'picture',
|
|
||||||
recommendationViewMode: 'picture',
|
|
||||||
historyViewMode: 'picture',
|
|
||||||
searchViewMode: 'picture',
|
|
||||||
password: '',
|
|
||||||
proxy: {
|
|
||||||
type: 'none',
|
|
||||||
scheme: '',
|
|
||||||
url: '',
|
|
||||||
port: ''
|
|
||||||
},
|
|
||||||
allowPassWhenIptvCheck: true,
|
|
||||||
autocleanWhenIptvCheck: false
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
const localKey = [
|
|
||||||
{
|
|
||||||
name: 'playAndPause',
|
|
||||||
desc: '播放或暂停',
|
|
||||||
key: 'space'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'forward',
|
|
||||||
desc: '快进',
|
|
||||||
key: 'right'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'back',
|
|
||||||
desc: '快退',
|
|
||||||
key: 'left'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'volumeUp',
|
|
||||||
desc: '音量调高',
|
|
||||||
key: 'up'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'volumeDown',
|
|
||||||
desc: '音量调低',
|
|
||||||
key: 'down'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'mute',
|
|
||||||
desc: '静音',
|
|
||||||
key: 'm'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'top',
|
|
||||||
desc: '置顶或退出置顶',
|
|
||||||
key: 't'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'fullscreen',
|
|
||||||
desc: '进入或退出全屏',
|
|
||||||
key: 'f'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'escape',
|
|
||||||
desc: '退出全屏/精简模式',
|
|
||||||
key: 'esc'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'next',
|
|
||||||
desc: '下一集',
|
|
||||||
key: 'alt+right'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'prev',
|
|
||||||
desc: '上一集',
|
|
||||||
key: 'alt+left'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'home',
|
|
||||||
desc: '跳到视频开始位置',
|
|
||||||
key: 'home'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'end',
|
|
||||||
desc: '跳到视频结束位置',
|
|
||||||
key: 'end'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'opacityUp',
|
|
||||||
desc: '透明度调高',
|
|
||||||
key: 'alt+up'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'opacityDown',
|
|
||||||
desc: '透明度调低',
|
|
||||||
key: 'alt+down'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'playbackRateUp',
|
|
||||||
desc: '播放倍速加快',
|
|
||||||
key: 'pageup'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'playbackRateDown',
|
|
||||||
desc: '播放倍速减慢',
|
|
||||||
key: 'pagedown'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'mini',
|
|
||||||
desc: '进入或退出mini模式',
|
|
||||||
key: 'alt+m'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'resetMini',
|
|
||||||
desc: '重置mini窗口',
|
|
||||||
key: 'ctrl+0'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
const getSite = (key) => {
|
const getSite = (key) => {
|
||||||
for (const i of sites) {
|
for (const i of sites) {
|
||||||
if (key === i.key) {
|
if (key === i.key) {
|
||||||
@@ -134,11 +9,13 @@ const getSite = (key) => {
|
|||||||
const sites = require('./iniData/Sites.json')
|
const sites = require('./iniData/Sites.json')
|
||||||
const iptv = require('./iniData/Iptv.json')
|
const iptv = require('./iniData/Iptv.json')
|
||||||
const recommendations = require('./iniData/Recommendations.json')
|
const recommendations = require('./iniData/Recommendations.json')
|
||||||
|
const iniSetting = require('./iniData/iniSetting.json')
|
||||||
|
const localKey = require('./iniData/localKey.json')
|
||||||
export {
|
export {
|
||||||
setting,
|
|
||||||
sites,
|
sites,
|
||||||
iptv,
|
iptv,
|
||||||
recommendations,
|
recommendations,
|
||||||
|
iniSetting,
|
||||||
localKey,
|
localKey,
|
||||||
getSite
|
getSite
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,12 @@ export default {
|
|||||||
async find () {
|
async find () {
|
||||||
return await setting.get({ id: 0 })
|
return await setting.get({ id: 0 })
|
||||||
},
|
},
|
||||||
|
async bulkAdd (doc) {
|
||||||
|
return await setting.bulkAdd(doc)
|
||||||
|
},
|
||||||
|
async add (doc) {
|
||||||
|
return await setting.add(doc)
|
||||||
|
},
|
||||||
async update (docs) {
|
async update (docs) {
|
||||||
return await setting.update(0, docs)
|
return await setting.update(0, docs)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
import { Message, Button, Table, TableColumn, Tag, Input, Dialog, Form, FormItem, Switch, Select, Option, Checkbox, Autocomplete, Col, Tree } 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'
|
import Plugin from 'v-fit-columns'
|
||||||
|
import { ButtonGroupPlugin } from 'bootstrap-vue'
|
||||||
|
Vue.use(ButtonGroupPlugin)
|
||||||
Vue.use(Button)
|
Vue.use(Button)
|
||||||
Vue.use(Col)
|
Vue.use(Col)
|
||||||
Vue.use(Table)
|
Vue.use(Table)
|
||||||
Vue.use(TableColumn)
|
Vue.use(TableColumn)
|
||||||
Vue.use(Tag)
|
Vue.use(Tag)
|
||||||
Vue.use(Input)
|
Vue.use(Input)
|
||||||
|
Vue.use(InputNumber)
|
||||||
Vue.use(Dialog)
|
Vue.use(Dialog)
|
||||||
Vue.use(Form)
|
Vue.use(Form)
|
||||||
Vue.use(FormItem)
|
Vue.use(FormItem)
|
||||||
@@ -17,4 +20,7 @@ Vue.use(Option)
|
|||||||
Vue.use(Checkbox)
|
Vue.use(Checkbox)
|
||||||
Vue.use(Autocomplete)
|
Vue.use(Autocomplete)
|
||||||
Vue.use(Tree)
|
Vue.use(Tree)
|
||||||
|
Vue.use(Divider)
|
||||||
|
Vue.use(Progress)
|
||||||
Vue.prototype.$message = Message
|
Vue.prototype.$message = Message
|
||||||
|
Vue.prototype.$msgbox = MessageBox
|
||||||
|
|||||||
@@ -35,29 +35,29 @@ const onlineVideo = {
|
|||||||
},
|
},
|
||||||
playVideoOnBde4 (videoName, videoIndex) {
|
playVideoOnBde4 (videoName, videoIndex) {
|
||||||
videoName = videoName.replace(/\s/g, '')
|
videoName = videoName.replace(/\s/g, '')
|
||||||
var url = `https://bde4.com/search/${videoName}`
|
const url = `https://bde4.com/search/${videoName}`
|
||||||
axios.get(url).then(res => {
|
axios.get(url).then(res => {
|
||||||
const $ = cheerio.load(res.data)
|
const $ = cheerio.load(res.data)
|
||||||
var e = $('div.search-list')
|
const e = $('div.search-list')
|
||||||
var searchResult = $(e).find('div>div>div>div>a').toArray()
|
const searchResult = $(e).find('div>div>div>div>a').toArray()
|
||||||
// 获取第一个搜索结果的视频链接
|
// 获取第一个搜索结果的视频链接
|
||||||
var detailPageLink = $(searchResult[0]).attr('href')
|
const detailPageLink = $(searchResult[0]).attr('href')
|
||||||
// 获取第一个搜索结果的title
|
// 获取第一个搜索结果的title
|
||||||
var title = $(searchResult[0]).attr('title')
|
const title = $(searchResult[0]).attr('title')
|
||||||
if (title === null || title === undefined || !title.replace(/\s/g, '').includes(videoName)) {
|
if (title === null || title === undefined || !title.replace(/\s/g, '').includes(videoName)) {
|
||||||
// 如果第一个搜索结果不符合,打开搜索页面
|
// 如果第一个搜索结果不符合,打开搜索页面
|
||||||
open(url)
|
open(url)
|
||||||
} else {
|
} else {
|
||||||
var detailPageFullLink = 'https://bde4.com/' + detailPageLink
|
const detailPageFullLink = 'https://bde4.com/' + detailPageLink
|
||||||
// 解析详情页面
|
// 解析详情页面
|
||||||
axios.get(detailPageFullLink).then(res => {
|
axios.get(detailPageFullLink).then(res => {
|
||||||
const $ = cheerio.load(res.data)
|
const $ = cheerio.load(res.data)
|
||||||
var e = $('div.info1')
|
const d = $('div.info1')
|
||||||
var videoList = $(e).find('a').toArray()
|
const videoList = $(d).find('a').toArray()
|
||||||
var videoFullLink = detailPageFullLink
|
let videoFullLink = detailPageFullLink
|
||||||
// 获取index视频链接
|
// 获取index视频链接
|
||||||
if (videoIndex < videoList.length) {
|
if (videoIndex < videoList.length) {
|
||||||
var indexVideoLink = $(videoList[videoIndex]).attr('href')
|
const indexVideoLink = $(videoList[videoIndex]).attr('href')
|
||||||
if (indexVideoLink.includes('.htm')) {
|
if (indexVideoLink.includes('.htm')) {
|
||||||
videoFullLink = 'https://bde4.com' + indexVideoLink
|
videoFullLink = 'https://bde4.com' + indexVideoLink
|
||||||
}
|
}
|
||||||
@@ -69,31 +69,31 @@ const onlineVideo = {
|
|||||||
},
|
},
|
||||||
playVideoOnK1080 (videoName, videoIndex) {
|
playVideoOnK1080 (videoName, videoIndex) {
|
||||||
videoName = videoName.replace(/\s/g, '')
|
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 => {
|
axios.get(url).then(res => {
|
||||||
const $ = cheerio.load(res.data)
|
const $ = cheerio.load(res.data)
|
||||||
var e = $('#searchList')
|
const e = $('#searchList')
|
||||||
var searchResult = $(e).find('li>div>a').toArray()
|
const searchResult = $(e).find('li>div>a').toArray()
|
||||||
// 获取第一个搜索结果的视频链接
|
// 获取第一个搜索结果的视频链接
|
||||||
var detailPageLink = $(searchResult[0]).attr('href')
|
const detailPageLink = $(searchResult[0]).attr('href')
|
||||||
// 获取第一个搜索结果的title
|
// 获取第一个搜索结果的title
|
||||||
var title = $(searchResult[0]).attr('title')
|
const title = $(searchResult[0]).attr('title')
|
||||||
if (title === null || title === undefined || !title.replace(/\s/g, '').includes(videoName)) {
|
if (title === null || title === undefined || !title.replace(/\s/g, '').includes(videoName)) {
|
||||||
// 如果第一个搜索结果不符合,打开搜索页面
|
// 如果第一个搜索结果不符合,打开搜索页面
|
||||||
open(url)
|
open(url)
|
||||||
} else {
|
} else {
|
||||||
// 解析详情页面
|
// 解析详情页面
|
||||||
var detailPageFullLink = 'https://k1080.net' + detailPageLink
|
const detailPageFullLink = 'https://k1080.net' + detailPageLink
|
||||||
axios.get(detailPageFullLink).then(res2 => {
|
axios.get(detailPageFullLink).then(res2 => {
|
||||||
const $ = cheerio.load(res2.data)
|
const $ = cheerio.load(res2.data)
|
||||||
// 获取playlist1
|
// 获取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()
|
||||||
var videoFullLink = detailPageFullLink
|
let videoFullLink = detailPageFullLink
|
||||||
// 获取index视频链接
|
// 获取index视频链接
|
||||||
if (videoIndex < videoList.length) {
|
if (videoIndex < videoList.length) {
|
||||||
var indexVideoLink = $(videoList[videoIndex]).attr('href')
|
const indexVideoLink = $(videoList[videoIndex]).attr('href')
|
||||||
if (indexVideoLink.includes('.htm')) {
|
if (indexVideoLink.includes('.htm')) {
|
||||||
videoFullLink = 'https://k1080.net' + indexVideoLink
|
videoFullLink = 'https://k1080.net' + indexVideoLink
|
||||||
}
|
}
|
||||||
@@ -105,31 +105,31 @@ const onlineVideo = {
|
|||||||
},
|
},
|
||||||
playVideoOnSubaibai (videoName, videoIndex) {
|
playVideoOnSubaibai (videoName, videoIndex) {
|
||||||
videoName = videoName.replace(/\s/g, '')
|
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 => {
|
axios.get(url).then(res => {
|
||||||
const $ = cheerio.load(res.data)
|
const $ = cheerio.load(res.data)
|
||||||
var e = $('div.search_list')
|
const e = $('div.search_list')
|
||||||
var searchResult = $(e).find('div>ul>li>h3>a').toArray()
|
const searchResult = $(e).find('div>ul>li>h3>a').toArray()
|
||||||
// 获取第一个搜索结果的视频链接
|
// 获取第一个搜索结果的视频链接
|
||||||
var detailPageLink = $(searchResult[0]).attr('href')
|
const detailPageLink = $(searchResult[0]).attr('href')
|
||||||
// 获取第一个搜索结果的title
|
// 获取第一个搜索结果的title
|
||||||
var title = $(searchResult[0]).text()
|
const title = $(searchResult[0]).text()
|
||||||
if (title === null || title === undefined || !title.replace(/\s/g, '').includes(videoName)) {
|
if (title === null || title === undefined || !title.replace(/\s/g, '').includes(videoName)) {
|
||||||
// 如果第一个搜索结果不符合,打开搜索页面
|
// 如果第一个搜索结果不符合,打开搜索页面
|
||||||
open(url)
|
open(url)
|
||||||
} else {
|
} else {
|
||||||
// 解析详情页面
|
// 解析详情页面
|
||||||
var detailPageFullLink = detailPageLink
|
const detailPageFullLink = detailPageLink
|
||||||
axios.get(detailPageFullLink).then(res2 => {
|
axios.get(detailPageFullLink).then(res2 => {
|
||||||
const $ = cheerio.load(res2.data)
|
const $ = cheerio.load(res2.data)
|
||||||
// 获取playlist1
|
// 获取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视频链接
|
// 获取index视频链接
|
||||||
var videoFullLink = detailPageFullLink
|
let videoFullLink = detailPageFullLink
|
||||||
if (videoIndex < videoList.length) {
|
if (videoIndex < videoList.length) {
|
||||||
var indexVideoLink = $(videoList[videoIndex]).attr('href')
|
const indexVideoLink = $(videoList[videoIndex]).attr('href')
|
||||||
if (indexVideoLink.includes('.htm')) {
|
if (indexVideoLink.includes('.htm')) {
|
||||||
videoFullLink = indexVideoLink
|
videoFullLink = indexVideoLink
|
||||||
}
|
}
|
||||||
@@ -141,31 +141,31 @@ const onlineVideo = {
|
|||||||
},
|
},
|
||||||
playVideoOnYhdm (videoName, videoIndex) {
|
playVideoOnYhdm (videoName, videoIndex) {
|
||||||
videoName = videoName.replace(/\s/g, '')
|
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 => {
|
axios.get(url).then(res => {
|
||||||
const $ = cheerio.load(res.data)
|
const $ = cheerio.load(res.data)
|
||||||
var e = $('div.lpic')
|
const e = $('div.lpic')
|
||||||
var searchResult = $(e).find('div>ul>li>h2>a').toArray()
|
const searchResult = $(e).find('div>ul>li>h2>a').toArray()
|
||||||
// 获取第一个搜索结果的视频链接
|
// 获取第一个搜索结果的视频链接
|
||||||
var detailPageLink = $(searchResult[0]).attr('href')
|
const detailPageLink = $(searchResult[0]).attr('href')
|
||||||
// 获取第一个搜索结果的title
|
// 获取第一个搜索结果的title
|
||||||
var title = $(searchResult[0]).attr('title')
|
const title = $(searchResult[0]).attr('title')
|
||||||
if (title === null || title === undefined || !title.replace(/\s/g, '').includes(videoName)) {
|
if (title === null || title === undefined || !title.replace(/\s/g, '').includes(videoName)) {
|
||||||
// 如果第一个搜索结果不符合,打开搜索页面
|
// 如果第一个搜索结果不符合,打开搜索页面
|
||||||
open(url)
|
open(url)
|
||||||
} else {
|
} else {
|
||||||
// 解析详情页面
|
// 解析详情页面
|
||||||
var detailPageFullLink = 'http://www.yhdm.tv/' + detailPageLink
|
const detailPageFullLink = 'http://www.yhdm.tv/' + detailPageLink
|
||||||
axios.get(detailPageFullLink).then(res2 => {
|
axios.get(detailPageFullLink).then(res2 => {
|
||||||
const $ = cheerio.load(res2.data)
|
const $ = cheerio.load(res2.data)
|
||||||
// 获取playlist1
|
// 获取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视频链接
|
// 获取index视频链接
|
||||||
var videoFullLink = detailPageFullLink
|
let videoFullLink = detailPageFullLink
|
||||||
if (videoIndex < videoList.length) {
|
if (videoIndex < videoList.length) {
|
||||||
var indexVideoLink = $(videoList[videoIndex]).attr('href')
|
const indexVideoLink = $(videoList[videoIndex]).attr('href')
|
||||||
if (indexVideoLink.includes('.htm')) {
|
if (indexVideoLink.includes('.htm')) {
|
||||||
videoFullLink = 'http://www.yhdm.tv/' + indexVideoLink
|
videoFullLink = 'http://www.yhdm.tv/' + indexVideoLink
|
||||||
}
|
}
|
||||||
@@ -177,31 +177,31 @@ const onlineVideo = {
|
|||||||
},
|
},
|
||||||
playVideoOndmdm2020 (videoName, videoIndex) {
|
playVideoOndmdm2020 (videoName, videoIndex) {
|
||||||
videoName = videoName.replace(/\s/g, '')
|
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 => {
|
axios.get(url).then(res => {
|
||||||
const $ = cheerio.load(res.data)
|
const $ = cheerio.load(res.data)
|
||||||
var e = $('#searchList')
|
const e = $('#searchList')
|
||||||
var searchResult = $(e).find('ul>li>div>h4>a').toArray()
|
const searchResult = $(e).find('ul>li>div>h4>a').toArray()
|
||||||
// 获取第一个搜索结果的视频链接
|
// 获取第一个搜索结果的视频链接
|
||||||
var detailPageLink = $(searchResult[0]).attr('href')
|
const detailPageLink = $(searchResult[0]).attr('href')
|
||||||
// 获取第一个搜索结果的title
|
// 获取第一个搜索结果的title
|
||||||
var title = $(searchResult[0]).text()
|
const title = $(searchResult[0]).text()
|
||||||
if (title === null || title === undefined || !title.replace(/\s/g, '').includes(videoName)) {
|
if (title === null || title === undefined || !title.replace(/\s/g, '').includes(videoName)) {
|
||||||
// 如果第一个搜索结果不符合,打开搜索页面
|
// 如果第一个搜索结果不符合,打开搜索页面
|
||||||
open(url)
|
open(url)
|
||||||
} else {
|
} else {
|
||||||
// 解析详情页面
|
// 解析详情页面
|
||||||
var detailPageFullLink = 'http://www.dmdm2020.com' + detailPageLink
|
const detailPageFullLink = 'http://www.dmdm2020.com' + detailPageLink
|
||||||
axios.get(detailPageFullLink).then(res2 => {
|
axios.get(detailPageFullLink).then(res2 => {
|
||||||
const $ = cheerio.load(res2.data)
|
const $ = cheerio.load(res2.data)
|
||||||
// 获取playlist1
|
// 获取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视频链接
|
// 获取index视频链接
|
||||||
var videoFullLink = detailPageFullLink
|
let videoFullLink = detailPageFullLink
|
||||||
if (videoIndex < videoList.length) {
|
if (videoIndex < videoList.length) {
|
||||||
var indexVideoLink = $(videoList[videoIndex]).attr('href')
|
const indexVideoLink = $(videoList[videoIndex]).attr('href')
|
||||||
if (indexVideoLink.includes('.htm')) {
|
if (indexVideoLink.includes('.htm')) {
|
||||||
videoFullLink = 'http://www.dmdm2020.com' + indexVideoLink
|
videoFullLink = 'http://www.dmdm2020.com' + indexVideoLink
|
||||||
}
|
}
|
||||||
@@ -213,31 +213,31 @@ const onlineVideo = {
|
|||||||
},
|
},
|
||||||
playVideoOnSyrme (videoName, videoIndex) {
|
playVideoOnSyrme (videoName, videoIndex) {
|
||||||
videoName = videoName.replace(/\s/g, '')
|
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 => {
|
axios.get(url).then(res => {
|
||||||
const $ = cheerio.load(res.data)
|
const $ = cheerio.load(res.data)
|
||||||
var e = $('ul.MovieList')
|
const e = $('ul.MovieList')
|
||||||
var searchResult = $(e).find('ul>li>article>a').toArray()
|
const searchResult = $(e).find('ul>li>article>a').toArray()
|
||||||
// 获取第一个搜索结果的视频链接
|
// 获取第一个搜索结果的视频链接
|
||||||
var detailPageLink = $(searchResult[0]).attr('href')
|
const detailPageLink = $(searchResult[0]).attr('href')
|
||||||
// 获取第一个搜索结果的title
|
// 获取第一个搜索结果的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)) {
|
if (title === null || title === undefined || !title.replace(/\s/g, '').includes(videoName)) {
|
||||||
// 如果第一个搜索结果不符合,打开搜索页面
|
// 如果第一个搜索结果不符合,打开搜索页面
|
||||||
open(url)
|
open(url)
|
||||||
} else {
|
} else {
|
||||||
// 解析详情页面
|
// 解析详情页面
|
||||||
var detailPageFullLink = 'https://syrme.top' + detailPageLink
|
const detailPageFullLink = 'https://syrme.top' + detailPageLink
|
||||||
axios.get(detailPageFullLink).then(res2 => {
|
axios.get(detailPageFullLink).then(res2 => {
|
||||||
const $ = cheerio.load(res2.data)
|
const $ = cheerio.load(res2.data)
|
||||||
// 获取playlist1
|
// 获取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视频链接
|
// 获取index视频链接
|
||||||
var videoFullLink = detailPageFullLink
|
let videoFullLink = detailPageFullLink
|
||||||
if (videoIndex < videoList.length) {
|
if (videoIndex < videoList.length) {
|
||||||
var indexVideoLink = $(videoList[videoIndex]).attr('href')
|
const indexVideoLink = $(videoList[videoIndex]).attr('href')
|
||||||
videoFullLink = 'https://syrme.top' + indexVideoLink
|
videoFullLink = 'https://syrme.top' + indexVideoLink
|
||||||
}
|
}
|
||||||
open(videoFullLink)
|
open(videoFullLink)
|
||||||
@@ -247,31 +247,31 @@ const onlineVideo = {
|
|||||||
},
|
},
|
||||||
playVideoOnJpysvip (videoName, videoIndex) {
|
playVideoOnJpysvip (videoName, videoIndex) {
|
||||||
videoName = videoName.replace(/\s/g, '')
|
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 => {
|
axios.get(url).then(res => {
|
||||||
const $ = cheerio.load(res.data)
|
const $ = cheerio.load(res.data)
|
||||||
var e = $('#searchList')
|
const e = $('#searchList')
|
||||||
var searchResult = $(e).find('ul>li>div>a').toArray()
|
const searchResult = $(e).find('ul>li>div>a').toArray()
|
||||||
// 获取第一个搜索结果的视频链接
|
// 获取第一个搜索结果的视频链接
|
||||||
var detailPageLink = $(searchResult[0]).attr('href')
|
const detailPageLink = $(searchResult[0]).attr('href')
|
||||||
// 获取第一个搜索结果的title
|
// 获取第一个搜索结果的title
|
||||||
var title = $(searchResult[0]).attr('title')
|
const title = $(searchResult[0]).attr('title')
|
||||||
if (title === null || title === undefined || !title.replace(/\s/g, '').includes(videoName)) {
|
if (title === null || title === undefined || !title.replace(/\s/g, '').includes(videoName)) {
|
||||||
// 如果第一个搜索结果不符合,打开搜索页面
|
// 如果第一个搜索结果不符合,打开搜索页面
|
||||||
open(url)
|
open(url)
|
||||||
} else {
|
} else {
|
||||||
// 解析详情页面
|
// 解析详情页面
|
||||||
var detailPageFullLink = 'https://www.jpysvip.net' + detailPageLink
|
const detailPageFullLink = 'https://www.jpysvip.net' + detailPageLink
|
||||||
axios.get(detailPageFullLink).then(res2 => {
|
axios.get(detailPageFullLink).then(res2 => {
|
||||||
const $ = cheerio.load(res2.data)
|
const $ = cheerio.load(res2.data)
|
||||||
// 获取playlist1
|
// 获取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视频链接
|
// 获取index视频链接
|
||||||
var videoFullLink = detailPageFullLink
|
let videoFullLink = detailPageFullLink
|
||||||
if (videoIndex < videoList.length) {
|
if (videoIndex < videoList.length) {
|
||||||
var indexVideoLink = $(videoList[videoIndex]).attr('href')
|
const indexVideoLink = $(videoList[videoIndex]).attr('href')
|
||||||
videoFullLink = 'https://www.jpysvip.net/' + indexVideoLink
|
videoFullLink = 'https://www.jpysvip.net/' + indexVideoLink
|
||||||
}
|
}
|
||||||
open(videoFullLink)
|
open(videoFullLink)
|
||||||
@@ -281,31 +281,31 @@ const onlineVideo = {
|
|||||||
},
|
},
|
||||||
playVideoOnXhkan (videoName, videoIndex) {
|
playVideoOnXhkan (videoName, videoIndex) {
|
||||||
videoName = videoName.replace(/\s/g, '')
|
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 => {
|
axios.get(url).then(res => {
|
||||||
const $ = cheerio.load(res.data)
|
const $ = cheerio.load(res.data)
|
||||||
var e = $('#searchList')
|
const e = $('#searchList')
|
||||||
var searchResult = $(e).find('ul>li>div>a').toArray()
|
const searchResult = $(e).find('ul>li>div>a').toArray()
|
||||||
// 获取第一个搜索结果的视频链接
|
// 获取第一个搜索结果的视频链接
|
||||||
var detailPageLink = $(searchResult[0]).attr('href')
|
const detailPageLink = $(searchResult[0]).attr('href')
|
||||||
// 获取第一个搜索结果的title
|
// 获取第一个搜索结果的title
|
||||||
var title = $(searchResult[0]).attr('title')
|
const title = $(searchResult[0]).attr('title')
|
||||||
if (title === null || title === undefined || !title.replace(/\s/g, '').includes(videoName)) {
|
if (title === null || title === undefined || !title.replace(/\s/g, '').includes(videoName)) {
|
||||||
// 如果第一个搜索结果不符合,打开搜索页面
|
// 如果第一个搜索结果不符合,打开搜索页面
|
||||||
open(url)
|
open(url)
|
||||||
} else {
|
} else {
|
||||||
// 解析详情页面
|
// 解析详情页面
|
||||||
var detailPageFullLink = detailPageLink
|
const detailPageFullLink = detailPageLink
|
||||||
axios.get(detailPageFullLink).then(res2 => {
|
axios.get(detailPageFullLink).then(res2 => {
|
||||||
const $ = cheerio.load(res2.data)
|
const $ = cheerio.load(res2.data)
|
||||||
// 获取playlist1
|
// 获取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视频链接
|
// 获取index视频链接
|
||||||
var videoFullLink = detailPageFullLink
|
let videoFullLink = detailPageFullLink
|
||||||
if (videoIndex < videoList.length) {
|
if (videoIndex < videoList.length) {
|
||||||
var indexVideoLink = $(videoList[videoIndex]).attr('href')
|
const indexVideoLink = $(videoList[videoIndex]).attr('href')
|
||||||
videoFullLink = indexVideoLink
|
videoFullLink = indexVideoLink
|
||||||
}
|
}
|
||||||
open(videoFullLink)
|
open(videoFullLink)
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
import express from 'express'
|
|
||||||
import cors from 'cors'
|
|
||||||
const Axios = require('axios')
|
|
||||||
|
|
||||||
const app = express()
|
|
||||||
app.use(cors())
|
|
||||||
app.use(express.json())
|
|
||||||
app.use(express.urlencoded({ extended: true }))
|
|
||||||
|
|
||||||
app.post('/api', async (req, res) => {
|
|
||||||
const result = await Axios.get(req.body.url)
|
|
||||||
res.json({
|
|
||||||
code: 1,
|
|
||||||
info: result.data
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
app.listen(44444)
|
|
||||||
@@ -3,36 +3,55 @@ import axios from 'axios'
|
|||||||
import parser from 'fast-xml-parser'
|
import parser from 'fast-xml-parser'
|
||||||
import cheerio from 'cheerio'
|
import cheerio from 'cheerio'
|
||||||
import { Parser as M3u8Parser } from 'm3u8-parser'
|
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/
|
// 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
|
// xgplayer使用chromium代理设置,浏览器又默认使用系统代理 https://www.chromium.org/developers/design-documents/network-settings
|
||||||
// 要在设置中添加代理设置,可参考https://stackoverflow.com/questions/37393248/how-connect-to-proxy-in-electron-webview
|
// 要在设置中添加代理设置,可参考https://stackoverflow.com/questions/37393248/how-connect-to-proxy-in-electron-webview
|
||||||
var http = require('http')
|
const http = require('http')
|
||||||
var https = require('http')
|
const https = require('http')
|
||||||
const { remote } = require('electron')
|
const remote = require('@electron/remote')
|
||||||
var win = remote.getCurrentWindow()
|
const win = remote.getCurrentWindow()
|
||||||
var session = win.webContents.session
|
const session = win.webContents.session
|
||||||
var ElectronProxyAgent = require('electron-proxy-agent')
|
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 // 可能使用代理,增长超时
|
// axios.defaults.timeout = 10000 // 可能使用代理,增长超时
|
||||||
|
const TIMEOUT = 20000
|
||||||
|
|
||||||
// 重试次数,共请求3次
|
// 重试次数,共请求2次
|
||||||
axios.defaults.retry = 2
|
axios.defaults.retry = 1
|
||||||
|
|
||||||
// 请求的间隙
|
// 请求的间隙
|
||||||
axios.defaults.retryDelay = 1000
|
axios.defaults.retryDelay = 1000
|
||||||
|
|
||||||
|
// 使用请求拦截器动态调整超时
|
||||||
|
axios.interceptors.request.use(function (config) {
|
||||||
|
if (config.__retryCount === undefined) {
|
||||||
|
config.timeout = TIMEOUT
|
||||||
|
} else {
|
||||||
|
config.timeout = TIMEOUT * (config.__retryCount + 1)
|
||||||
|
}
|
||||||
|
return config
|
||||||
|
}, function (err) {
|
||||||
|
return Promise.reject(err)
|
||||||
|
})
|
||||||
|
|
||||||
// 添加响应拦截器
|
// 添加响应拦截器
|
||||||
axios.interceptors.response.use(function (response) {
|
axios.interceptors.response.use(function (response) {
|
||||||
// 对响应数据做些事
|
|
||||||
if (response.status && response.status === 200 && response.request.responseURL.includes('api.php') && !response.data.startsWith('<?xml')) {
|
|
||||||
}
|
|
||||||
return response
|
return response
|
||||||
}, function (err) { // 请求错误时做些事
|
}, function (err) { // 请求错误时做些事
|
||||||
// 请求超时的之后,抛出 err.code = ECONNABORTED的错误..错误信息是 timeout of xxx ms exceeded
|
// 请求超时的之后,抛出 err.code = ECONNABORTED的错误..错误信息是 timeout of xxx ms exceeded
|
||||||
if (err.code === 'ECONNABORTED' && err.message.indexOf('timeout') !== -1) {
|
if (err.code === 'ECONNABORTED' && err.message.indexOf('timeout') !== -1) {
|
||||||
var config = err.config
|
const config = err.config
|
||||||
config.__retryCount = config.__retryCount || 0
|
config.__retryCount = config.__retryCount || 0
|
||||||
|
|
||||||
if (config.__retryCount >= config.retry) {
|
if (config.__retryCount >= config.retry) {
|
||||||
@@ -42,7 +61,7 @@ axios.interceptors.response.use(function (response) {
|
|||||||
|
|
||||||
config.__retryCount += 1
|
config.__retryCount += 1
|
||||||
|
|
||||||
var backoff = new Promise(function (resolve) {
|
const backoff = new Promise(function (resolve) {
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
resolve()
|
resolve()
|
||||||
}, config.retryDelay || 1)
|
}, config.retryDelay || 1)
|
||||||
@@ -89,16 +108,19 @@ const zy = {
|
|||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
this.getSite(key).then(res => {
|
this.getSite(key).then(res => {
|
||||||
const url = res.api
|
const url = res.api
|
||||||
axios.post(url).then(res => {
|
axios.get(url).then(res => {
|
||||||
const data = res.data
|
const data = res.data
|
||||||
const json = parser.parse(data, this.xmlConfig)
|
const json = parser.parse(data, this.xmlConfig)
|
||||||
const jsondata = json.rss === undefined ? json : json.rss
|
const jsondata = json?.rss === undefined ? json : json.rss
|
||||||
|
if (!jsondata?.class || !jsondata?.list) resolve()
|
||||||
const arr = []
|
const arr = []
|
||||||
if (jsondata.class) {
|
if (jsondata.class) {
|
||||||
|
// 有些网站返回的分类名里会含有一串包含在{}内的字符串,移除掉
|
||||||
|
const regex = /\{.*\}/i
|
||||||
for (const i of jsondata.class.ty) {
|
for (const i of jsondata.class.ty) {
|
||||||
const j = {
|
const j = {
|
||||||
tid: i._id,
|
tid: i._id,
|
||||||
name: i._t
|
name: i._t.replace(regex, '')
|
||||||
}
|
}
|
||||||
arr.push(j)
|
arr.push(j)
|
||||||
}
|
}
|
||||||
@@ -134,12 +156,16 @@ const zy = {
|
|||||||
} else {
|
} else {
|
||||||
url = `${site.api}?ac=videolist&pg=${pg}`
|
url = `${site.api}?ac=videolist&pg=${pg}`
|
||||||
}
|
}
|
||||||
axios.post(url).then(async res => {
|
axios.get(url).then(async res => {
|
||||||
const data = res.data
|
const data = res.data
|
||||||
const json = parser.parse(data, this.xmlConfig)
|
const json = parser.parse(data, this.xmlConfig)
|
||||||
const jsondata = json.rss === undefined ? json : json.rss
|
const jsondata = json.rss === undefined ? json : json.rss
|
||||||
const videoList = jsondata.list.video
|
const videoList = jsondata.list.video
|
||||||
resolve(videoList)
|
if (videoList && videoList.length) {
|
||||||
|
resolve(videoList)
|
||||||
|
} else {
|
||||||
|
resolve([])
|
||||||
|
}
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
reject(err)
|
reject(err)
|
||||||
})
|
})
|
||||||
@@ -162,8 +188,8 @@ const zy = {
|
|||||||
} else {
|
} else {
|
||||||
url = `${site.api}?ac=videolist`
|
url = `${site.api}?ac=videolist`
|
||||||
}
|
}
|
||||||
axios.post(url).then(async res => {
|
axios.get(url).then(async res => {
|
||||||
const data = res.data
|
const data = res.data.match(/<list [^>]*>/)[0] + '</list>' // 某些源站不含页码时获取到的数据parser无法解析
|
||||||
const json = parser.parse(data, this.xmlConfig)
|
const json = parser.parse(data, this.xmlConfig)
|
||||||
const jsondata = json.rss === undefined ? json : json.rss
|
const jsondata = json.rss === undefined ? json : json.rss
|
||||||
const pg = {
|
const pg = {
|
||||||
@@ -189,15 +215,59 @@ const zy = {
|
|||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
this.getSite(key).then(res => {
|
this.getSite(key).then(res => {
|
||||||
const site = res
|
const site = res
|
||||||
wd = encodeURI(wd)
|
const url = `${site.api}?wd=${encodeURI(wd)}`
|
||||||
var url = `${site.api}?wd=${wd}`
|
axios.get(url, { timeout: 3000 }).then(res => {
|
||||||
axios.post(url, { timeout: 3000 }).then(res => {
|
|
||||||
const data = res.data
|
const data = res.data
|
||||||
const json = parser.parse(data, this.xmlConfig)
|
const json = parser.parse(data, this.xmlConfig)
|
||||||
const jsondata = json.rss === undefined ? json : json.rss
|
const jsondata = json?.rss === undefined ? json : json.rss
|
||||||
if (json && jsondata && jsondata.list) {
|
if (json && jsondata && jsondata.list) {
|
||||||
const videoList = jsondata.list.video
|
let videoList = jsondata.list.video
|
||||||
resolve(videoList)
|
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()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
resolve()
|
||||||
|
}
|
||||||
|
}).catch(err => {
|
||||||
|
reject(err)
|
||||||
|
})
|
||||||
|
}).catch(err => {
|
||||||
|
reject(err)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 搜索资源详情
|
||||||
|
* @param {*} key 资源网 key
|
||||||
|
* @param {*} wd 搜索关键字
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
searchFirstDetail (key, wd) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
this.getSite(key).then(res => {
|
||||||
|
const site = res
|
||||||
|
const url = `${site.api}?wd=${encodeURI(wd)}`
|
||||||
|
axios.get(url, { timeout: 3000 }).then(res => {
|
||||||
|
const data = res.data
|
||||||
|
const json = parser.parse(data, this.xmlConfig)
|
||||||
|
const jsondata = json?.rss === undefined ? json : json.rss
|
||||||
|
if (json && jsondata && jsondata.list) {
|
||||||
|
let videoList = jsondata.list.video
|
||||||
|
if (Object.prototype.toString.call(videoList) === '[object Object]') videoList = [].concat(videoList)
|
||||||
|
videoList = videoList?.filter(e => e.name.toLowerCase().includes(wd.toLowerCase()))
|
||||||
|
if (videoList?.length) {
|
||||||
|
this.detail(key, videoList[0].id).then(detailRes => {
|
||||||
|
resolve(detailRes)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
resolve()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
resolve()
|
||||||
}
|
}
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
reject(err)
|
reject(err)
|
||||||
@@ -217,26 +287,56 @@ const zy = {
|
|||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
this.getSite(key).then(res => {
|
this.getSite(key).then(res => {
|
||||||
const url = `${res.api}?ac=videolist&ids=${id}`
|
const url = `${res.api}?ac=videolist&ids=${id}`
|
||||||
axios.post(url).then(res => {
|
axios.get(url).then(res => {
|
||||||
const data = res.data
|
const data = res.data
|
||||||
const json = parser.parse(data, this.xmlConfig)
|
const json = parser.parse(data, this.xmlConfig)
|
||||||
const jsondata = json.rss === undefined ? json : json.rss
|
const jsondata = json?.rss === undefined ? json : json.rss
|
||||||
const videoList = jsondata.list.video
|
const videoList = jsondata?.list?.video
|
||||||
// Parse m3u8List
|
if (!videoList) resolve()
|
||||||
var m3u8List = []
|
// Parse video lists
|
||||||
|
let fullList = []
|
||||||
|
let index = 0
|
||||||
|
const supportedFormats = ['m3u8', 'mp4']
|
||||||
const dd = videoList.dl.dd
|
const dd = videoList.dl.dd
|
||||||
const type = Object.prototype.toString.call(dd)
|
const type = Object.prototype.toString.call(dd)
|
||||||
if (type === '[object Array]') {
|
if (type === '[object Array]') {
|
||||||
for (const i of dd) {
|
for (const i of dd) {
|
||||||
// 如果含有多个视频列表的话, 仅获取m3u8列表
|
i._t = i._t.replace(/\$+/g, '$')
|
||||||
if (i._flag.includes('m3u8')) {
|
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))
|
||||||
m3u8List = i._t.split('#')
|
if (ext.length && ext.length <= supportedFormats.length && ext.every(e => supportedFormats.includes(e))) {
|
||||||
|
if (ext.length === 1) {
|
||||||
|
i._flag = ext[0]
|
||||||
|
} else {
|
||||||
|
i._flag = index ? 'ZY支持-' + index : 'ZY支持'
|
||||||
|
index++
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
fullList.push(
|
||||||
|
{
|
||||||
|
flag: i._flag,
|
||||||
|
list: i._t.split('#').filter(e => e && (e.startsWith('http') || (e.split('$')[1] && e.split('$')[1].startsWith('http'))))
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
m3u8List = dd._t.split('#')
|
fullList.push(
|
||||||
|
{
|
||||||
|
flag: dd._flag,
|
||||||
|
list: dd._t.replace(/\$+/g, '$').split('#').filter(e => e && (e.startsWith('http') || (e.split('$')[1] && e.split('$')[1].startsWith('http'))))
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
videoList.m3u8List = m3u8List
|
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) {
|
||||||
|
const first = fullList.splice(index, 1)
|
||||||
|
fullList = first.concat(fullList)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
videoList.fullList = fullList
|
||||||
resolve(videoList)
|
resolve(videoList)
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
reject(err)
|
reject(err)
|
||||||
@@ -252,35 +352,55 @@ const zy = {
|
|||||||
* @param {*} id 资源唯一标识符 id
|
* @param {*} id 资源唯一标识符 id
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
download (key, id) {
|
download (key, id, videoFlag) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
let info = ''
|
||||||
|
let downloadUrls = ''
|
||||||
this.getSite(key).then(res => {
|
this.getSite(key).then(res => {
|
||||||
const site = res
|
const site = res
|
||||||
if (site.download) {
|
if (site.download) {
|
||||||
const url = `${site.download}?ac=videolist&ids=${id}&ct=1`
|
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 data = res.data
|
||||||
const json = parser.parse(data, this.xmlConfig)
|
const json = parser.parse(data, this.xmlConfig)
|
||||||
const jsondata = json.rss === undefined ? json : json.rss
|
const jsondata = json.rss === undefined ? json : json.rss
|
||||||
const videoList = jsondata.list.video
|
const videoList = jsondata.list.video
|
||||||
// Parse m3u8List
|
|
||||||
var m3u8List = []
|
|
||||||
const dd = videoList.dl.dd
|
const dd = videoList.dl.dd
|
||||||
const type = Object.prototype.toString.call(dd)
|
const type = Object.prototype.toString.call(dd)
|
||||||
if (type === '[object Array]') {
|
if (type === '[object Array]') {
|
||||||
for (const i of dd) {
|
for (const i of dd) {
|
||||||
m3u8List = i._t.split('#')
|
downloadUrls = i._t.replace(/\$+/g, '$').split('#').map(e => encodeURI(e.includes('$') ? e.split('$')[1] : e)).join('\n')
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
m3u8List = dd._t.split('#')
|
downloadUrls = dd._t.replace(/\$+/g, '$').split('#').map(e => encodeURI(e.includes('$') ? e.split('$')[1] : e)).join('\n')
|
||||||
}
|
}
|
||||||
videoList.m3u8List = m3u8List
|
if (downloadUrls) {
|
||||||
resolve(videoList)
|
info = '调用下载接口获取到的链接已复制, 快去下载吧!'
|
||||||
}).catch(err => {
|
resolve({ downloadUrls: downloadUrls, info: info })
|
||||||
|
} else {
|
||||||
|
throw new Error()
|
||||||
|
}
|
||||||
|
}).catch((err) => {
|
||||||
|
err.info = '无法获取到下载链接,请通过播放页面点击“调试”按钮获取'
|
||||||
reject(err)
|
reject(err)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
resolve([])
|
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.includes('$') ? i.split('$')[1] : i)
|
||||||
|
downloadUrls += (url + '\n')
|
||||||
|
}
|
||||||
|
if (downloadUrls) {
|
||||||
|
info = '视频源链接已复制, 快去下载吧!'
|
||||||
|
resolve({ downloadUrls: downloadUrls, info: info })
|
||||||
|
} else {
|
||||||
|
throw new Error()
|
||||||
|
}
|
||||||
|
}).catch((err) => {
|
||||||
|
err.info = '无法获取到下载链接,请通过播放页面点击“调试”按钮获取'
|
||||||
|
reject(err)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -307,49 +427,72 @@ const zy = {
|
|||||||
* @param {*} channel 直播频道 url
|
* @param {*} channel 直播频道 url
|
||||||
* @returns boolean
|
* @returns boolean
|
||||||
*/
|
*/
|
||||||
async checkChannel (channel) {
|
checkChannel (url) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios.get(channel).then(res => {
|
const supportFormats = /\.(m3u8|flv)$/
|
||||||
const manifest = res.data
|
const extRE = url.match(supportFormats) || new URL.URL(url).pathname.match(supportFormats)
|
||||||
var parser = new M3u8Parser()
|
if (extRE[1] === 'flv') {
|
||||||
parser.push(manifest)
|
const MAX_CONTENT_LENGTH = 2000 // axios配置maxContentLength不生效,先用request凑合
|
||||||
parser.end()
|
let receivedLength = 0
|
||||||
var parsedManifest = parser.manifest
|
let options = { uri: url, gzip: true, timeout: 10000 }
|
||||||
if (parsedManifest.segments.length) {
|
if (proxyURL) {
|
||||||
resolve(true)
|
if (proxyURL.startsWith('http')) options = Object.assign({ proxy: proxyURL }, options)
|
||||||
} else {
|
if (proxyURL.startsWith('socks5')) options = Object.assign({ agent: new SocksProxyAgent(proxyURL) }, options)
|
||||||
resolve(false)
|
|
||||||
}
|
}
|
||||||
}).catch(e => {
|
const req = request.get(options)
|
||||||
resolve(false)
|
.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)
|
||||||
|
})
|
||||||
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* 获取豆瓣页面链接
|
* 获取豆瓣页面链接
|
||||||
* @param {*} name 视频名称
|
* @param {*} name 视频名称
|
||||||
|
* @param {*} year 视频年份
|
||||||
* @returns 豆瓣页面链接,如果没有搜到该视频,返回搜索页面链接
|
* @returns 豆瓣页面链接,如果没有搜到该视频,返回搜索页面链接
|
||||||
*/
|
*/
|
||||||
doubanLink (name) {
|
doubanLink (name, year) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
// 豆瓣搜索链接
|
// 豆瓣搜索链接
|
||||||
var nameToSearch = name.replace(/\s/g, '')
|
const nameToSearch = name.replace(/\s/g, '')
|
||||||
var doubanSearchLink = 'https://www.douban.com/search?q=' + nameToSearch
|
const doubanSearchLink = 'https://www.douban.com/search?q=' + nameToSearch
|
||||||
axios.get(doubanSearchLink).then(res => {
|
axios.get(doubanSearchLink).then(res => {
|
||||||
const $ = cheerio.load(res.data)
|
const $ = cheerio.load(res.data)
|
||||||
// 比较第一和第二给豆瓣搜索结果, 看名字是否相符
|
// 查询所有搜索结果, 看名字和年代是否相符
|
||||||
var link = ''
|
let link = ''
|
||||||
var linkInDouban = $($('div.result')[0]).find('div>div>h3>a').first()
|
$('div.result').each(function () {
|
||||||
var nameInDouban = linkInDouban.text().replace(/\s/g, '')
|
const linkInDouban = $(this).find('div>div>h3>a').first()
|
||||||
if (nameToSearch === nameInDouban) {
|
const nameInDouban = linkInDouban.text().replace(/\s/g, '')
|
||||||
link = linkInDouban.attr('href')
|
const subjectCast = $(this).find('span.subject-cast').text()
|
||||||
} else {
|
if (nameToSearch === nameInDouban && subjectCast && subjectCast.includes(year)) {
|
||||||
linkInDouban = $($('div.result')[1]).find('div>div>h3>a').first()
|
|
||||||
nameInDouban = linkInDouban.text().replace(/\s/g, '')
|
|
||||||
if (nameToSearch === nameInDouban) {
|
|
||||||
link = linkInDouban.attr('href')
|
link = linkInDouban.attr('href')
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
if (link) {
|
if (link) {
|
||||||
resolve(link)
|
resolve(link)
|
||||||
} else {
|
} else {
|
||||||
@@ -364,18 +507,19 @@ const zy = {
|
|||||||
/**
|
/**
|
||||||
* 获取豆瓣评分
|
* 获取豆瓣评分
|
||||||
* @param {*} name 视频名称
|
* @param {*} name 视频名称
|
||||||
|
* @param {*} year 视频年份
|
||||||
* @returns 豆瓣评分
|
* @returns 豆瓣评分
|
||||||
*/
|
*/
|
||||||
doubanRate (name) {
|
doubanRate (name, year) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
var nameToSearch = name.replace(/\s/g, '')
|
const nameToSearch = name.replace(/\s/g, '')
|
||||||
this.doubanLink(nameToSearch).then(link => {
|
this.doubanLink(nameToSearch, year).then(link => {
|
||||||
if (link.includes('https://www.douban.com/search')) {
|
if (link.includes('https://www.douban.com/search')) {
|
||||||
resolve('暂无评分')
|
resolve('暂无评分')
|
||||||
} else {
|
} else {
|
||||||
axios.get(link).then(response => {
|
axios.get(link).then(response => {
|
||||||
const parsedHtml = cheerio.load(response.data)
|
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()) {
|
if (rating.text()) {
|
||||||
resolve(rating.text().replace(/\s/g, ''))
|
resolve(rating.text().replace(/\s/g, ''))
|
||||||
} else {
|
} else {
|
||||||
@@ -390,20 +534,55 @@ const zy = {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
async proxy () {
|
/**
|
||||||
|
* 获取豆瓣相关视频推荐列表
|
||||||
|
* @param {*} name 视频名称
|
||||||
|
* @param {*} year 视频年份
|
||||||
|
* @returns 豆瓣相关视频推荐列表
|
||||||
|
*/
|
||||||
|
doubanRecommendations (name, year) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const nameToSearch = name.replace(/\s/g, '')
|
||||||
|
const recommendations = []
|
||||||
|
this.doubanLink(nameToSearch, year).then(link => {
|
||||||
|
if (link.includes('https://www.douban.com/search')) {
|
||||||
|
resolve(recommendations)
|
||||||
|
} else {
|
||||||
|
axios.get(link).then(response => {
|
||||||
|
const $ = cheerio.load(response.data)
|
||||||
|
$('div.recommendations-bd').find('div>dl>dd>a').each(function (index, element) {
|
||||||
|
recommendations.push($(element).text())
|
||||||
|
})
|
||||||
|
resolve(recommendations)
|
||||||
|
}).catch(err => {
|
||||||
|
reject(err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}).catch(err => {
|
||||||
|
reject(err)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
getDefaultSites (url) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
axios.get(url).then(res => {
|
||||||
|
resolve(res.data)
|
||||||
|
}).catch(err => { reject(err) })
|
||||||
|
})
|
||||||
|
},
|
||||||
|
proxy () {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
setting.find().then(db => {
|
setting.find().then(db => {
|
||||||
if (db.proxy) {
|
if (db && db.proxy && db.proxy.type === 'manual') {
|
||||||
if (db.proxy.type === 'none') {
|
if (db.proxy.scheme && db.proxy.url && db.proxy.port) {
|
||||||
session.setProxy({ proxyRules: 'direct://' })
|
proxyURL = db.proxy.scheme + '://' + db.proxy.url.trim() + ':' + db.proxy.port.trim()
|
||||||
|
session.setProxy({ proxyRules: proxyURL })
|
||||||
http.globalAgent = https.globalAgent = new ElectronProxyAgent(session)
|
http.globalAgent = https.globalAgent = new ElectronProxyAgent(session)
|
||||||
} else if (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()
|
|
||||||
session.setProxy({ proxyRules: proxyURL })
|
|
||||||
http.globalAgent = https.globalAgent = new ElectronProxyAgent(session)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
proxyURL = ''
|
||||||
|
session.setProxy({ proxyRules: 'direct://' })
|
||||||
|
http.globalAgent = https.globalAgent = new ElectronProxyAgent(session)
|
||||||
}
|
}
|
||||||
// 不要删了,留着测试用
|
// 不要删了,留着测试用
|
||||||
// axios.get('https://api.my-ip.io/ip').then(res => console.log(res))
|
// axios.get('https://api.my-ip.io/ip').then(res => console.log(res))
|
||||||
|
|||||||
@@ -1,18 +1,24 @@
|
|||||||
import { BrowserWindow, ipcMain } from 'electron'
|
import { BrowserWindow, ipcMain } from 'electron'
|
||||||
import { autoUpdater } from 'electron-updater'
|
const { autoUpdater } = require('electron-updater')
|
||||||
|
|
||||||
|
// electron-updater 增量更新时似乎无法显示进度
|
||||||
export function initUpdater (win = BrowserWindow) {
|
export function initUpdater (win = BrowserWindow) {
|
||||||
autoUpdater.autoDownload = false
|
autoUpdater.autoDownload = false
|
||||||
autoUpdater.autoInstallOnAppQuit = false
|
autoUpdater.autoInstallOnAppQuit = true
|
||||||
|
|
||||||
// 主进程监听检查更新事件
|
// 主进程监听检查更新事件
|
||||||
ipcMain.on('checkForUpdate', () => {
|
ipcMain.on('checkForUpdate', () => {
|
||||||
autoUpdater.checkForUpdates()
|
autoUpdater.checkForUpdates()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 主进程监听开始下载事件
|
||||||
|
ipcMain.on('downloadUpdate', () => {
|
||||||
|
autoUpdater.downloadUpdate()
|
||||||
|
})
|
||||||
|
|
||||||
// 主进程监听退出并安装事件
|
// 主进程监听退出并安装事件
|
||||||
ipcMain.on('quitAndInstall', () => {
|
ipcMain.on('quitAndInstall', () => {
|
||||||
autoUpdater.downloadUpdate()
|
autoUpdater.quitAndInstall()
|
||||||
})
|
})
|
||||||
|
|
||||||
// 开始检测是否有更新
|
// 开始检测是否有更新
|
||||||
@@ -40,9 +46,8 @@ export function initUpdater (win = BrowserWindow) {
|
|||||||
win.webContents.send('download-progress', progressObj)
|
win.webContents.send('download-progress', progressObj)
|
||||||
})
|
})
|
||||||
|
|
||||||
// 下载完成并退出安装
|
// 下载完成
|
||||||
autoUpdater.on('update-downloaded', () => {
|
autoUpdater.on('update-downloaded', () => {
|
||||||
win.webContents.send('update-downloaded')
|
win.webContents.send('update-downloaded')
|
||||||
autoUpdater.quitAndInstall()
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,8 @@ export default new Vuex.Store({
|
|||||||
},
|
},
|
||||||
appState: {
|
appState: {
|
||||||
windowIsOnTop: false
|
windowIsOnTop: false
|
||||||
}
|
},
|
||||||
|
DetailCache: {}
|
||||||
},
|
},
|
||||||
getters: {
|
getters: {
|
||||||
getView: state => {
|
getView: state => {
|
||||||
@@ -48,6 +49,9 @@ export default new Vuex.Store({
|
|||||||
},
|
},
|
||||||
getAppState: state => {
|
getAppState: state => {
|
||||||
return state.appState
|
return state.appState
|
||||||
|
},
|
||||||
|
getDetailCache: state => {
|
||||||
|
return state.DetailCache
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mutations: {
|
mutations: {
|
||||||
@@ -68,6 +72,9 @@ export default new Vuex.Store({
|
|||||||
},
|
},
|
||||||
SET_APPSTATE: (state, payload) => {
|
SET_APPSTATE: (state, payload) => {
|
||||||
state.appState = payload
|
state.appState = payload
|
||||||
|
},
|
||||||
|
set_DetailCache: (state, payload) => {
|
||||||
|
state.DetailCache = payload
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||