Compare commits
653 Commits
main
...
multiThrea
| Author | SHA1 | Date |
|---|---|---|
|
|
3b83ebef35 | |
|
|
9d67a07a84 | |
|
|
12ac9fd628 | |
|
|
84a3f4399b | |
|
|
e467112e39 | |
|
|
6b0b4b6a74 | |
|
|
6c0841e4c5 | |
|
|
ffffe95c47 | |
|
|
d1bb29a314 | |
|
|
858579c462 | |
|
|
d280d770bb | |
|
|
d488a00eab | |
|
|
2e028809e8 | |
|
|
3002b235f9 | |
|
|
c28917b071 | |
|
|
61dcb1d4f3 | |
|
|
1c3929d920 | |
|
|
4b66ac8c19 | |
|
|
68b72532e8 | |
|
|
58ccf67b2d | |
|
|
cb27d8b6d0 | |
|
|
ed7963420f | |
|
|
6e56986b19 | |
|
|
661727b7f0 | |
|
|
eacd5fff22 | |
|
|
4a57536a3a | |
|
|
d31a069b85 | |
|
|
d0f6e7b601 | |
|
|
f90f88cfc5 | |
|
|
1ed471cb16 | |
|
|
514d49f2f5 | |
|
|
5dd19bc9e9 | |
|
|
77196c2037 | |
|
|
4f56a6f6d7 | |
|
|
0a90b60ce1 | |
|
|
f80c8ab7d1 | |
|
|
f0624a9014 | |
|
|
5130a64ba6 | |
|
|
235dfd716d | |
|
|
cf427b4224 | |
|
|
e5833f43a9 | |
|
|
8e0d555073 | |
|
|
c5e74b576a | |
|
|
d671e7090b | |
|
|
2b0bdcf9ce | |
|
|
c40988d997 | |
|
|
976fc63f4d | |
|
|
ebbe5360c3 | |
|
|
354d818fbf | |
|
|
00db8e8173 | |
|
|
4312efd414 | |
|
|
59b48044b7 | |
|
|
da79d74b06 | |
|
|
271c56767c | |
|
|
96272da9fc | |
|
|
2e95dbcc9c | |
|
|
6ec990b2f1 | |
|
|
537a84cc5b | |
|
|
bbb4d37922 | |
|
|
b753ae7fa4 | |
|
|
4204acc672 | |
|
|
3540ecc3aa | |
|
|
af044f37c4 | |
|
|
ed88ad03cf | |
|
|
2b88bf1c9d | |
|
|
b96fd2e894 | |
|
|
2d0ad541fc | |
|
|
9096420bac | |
|
|
f107976989 | |
|
|
089113fbe4 | |
|
|
7e26202bec | |
|
|
8a8991f03f | |
|
|
a0bd2a128a | |
|
|
394601dc07 | |
|
|
69ae330c7a | |
|
|
608e1b51a1 | |
|
|
58e158be75 | |
|
|
bb5c605425 | |
|
|
cff732fdac | |
|
|
448f2cf2c3 | |
|
|
2886403c73 | |
|
|
104a9c9b8a | |
|
|
36bcf9fbf1 | |
|
|
20d7bed2f9 | |
|
|
b5e302aa99 | |
|
|
bc672c7965 | |
|
|
39248d9a0a | |
|
|
08edebc431 | |
|
|
12b675da04 | |
|
|
93a5981c39 | |
|
|
240e9fb3b0 | |
|
|
26d525f436 | |
|
|
67a6cc2aa6 | |
|
|
565c92ce63 | |
|
|
7f49ff239f | |
|
|
0cdff801a4 | |
|
|
a3386d343a | |
|
|
52347dd0fd | |
|
|
3043f08f4c | |
|
|
ecd1467d33 | |
|
|
28a473a084 | |
|
|
cf45ab210c | |
|
|
4fb678c4bf | |
|
|
85bff735dd | |
|
|
12832816fe | |
|
|
d6ee012dff | |
|
|
1e7e846c5b | |
|
|
45b5dcdfba | |
|
|
bc388773d0 | |
|
|
03276cf9dd | |
|
|
537583e59b | |
|
|
9c00eb51d5 | |
|
|
683621c729 | |
|
|
5c4b3c3ab4 | |
|
|
4e7ba34997 | |
|
|
ab6b0f030c | |
|
|
7aaec80904 | |
|
|
986f2009f7 | |
|
|
8ac4501b1d | |
|
|
49b2c7c1e5 | |
|
|
c96d3dcca1 | |
|
|
b47e9b12ac | |
|
|
2d4d690431 | |
|
|
aabb9d7698 | |
|
|
001f56c842 | |
|
|
e38debf3c1 | |
|
|
586b4919c7 | |
|
|
35ab144e27 | |
|
|
0fd1d6df7c | |
|
|
fc1403c4a8 | |
|
|
debbcad7be | |
|
|
3449968aa8 | |
|
|
4e873ca2a9 | |
|
|
09324a563d | |
|
|
de261ec674 | |
|
|
62e00061fd | |
|
|
58b9479dc4 | |
|
|
9f224ab326 | |
|
|
4f31d98da5 | |
|
|
d15d2874e1 | |
|
|
445810ddc0 | |
|
|
1e2766edcb | |
|
|
c1443930da | |
|
|
228cd96bf3 | |
|
|
f9fee85737 | |
|
|
528376b09e | |
|
|
96f1dc0757 | |
|
|
5387abbd61 | |
|
|
05685aab0b | |
|
|
a87f9ca53d | |
|
|
6de76787e6 | |
|
|
d847d8ff49 | |
|
|
3ffe3e459d | |
|
|
80fc42168f | |
|
|
f706fa34af | |
|
|
2feb20da84 | |
|
|
f7b13b1b21 | |
|
|
932e6ebab5 | |
|
|
bb57f23567 | |
|
|
69555118cb | |
|
|
e67b2d874a | |
|
|
9afcc6e32a | |
|
|
1e139be660 | |
|
|
08b5acbf0a | |
|
|
33a100d5e6 | |
|
|
ccf4c0b068 | |
|
|
c9ebf3c7d7 | |
|
|
873e10b31e | |
|
|
b672af3b1d | |
|
|
dfe4a8f626 | |
|
|
1b1cb8e7a0 | |
|
|
e00bb67f74 | |
|
|
57abcc97b6 | |
|
|
f8101addf2 | |
|
|
eeac79f772 | |
|
|
45f3cfa1ab | |
|
|
e924e4f53b | |
|
|
d54be2ad8e | |
|
|
a578aaf6c3 | |
|
|
112eedbe36 | |
|
|
23ca8d26f1 | |
|
|
10c92b748c | |
|
|
dc70fd77d5 | |
|
|
4cb62ebba6 | |
|
|
16fb9d182e | |
|
|
d3ac6bd080 | |
|
|
01b7fbb09d | |
|
|
f5cce91a9c | |
|
|
a7485e82ae | |
|
|
e18a721e44 | |
|
|
226f41d386 | |
|
|
9e61895f05 | |
|
|
a328a51b03 | |
|
|
397b67a704 | |
|
|
9fcf1455e8 | |
|
|
2d2d3377c4 | |
|
|
8e35b95ac9 | |
|
|
b5a1f6ef04 | |
|
|
7e65d5eae0 | |
|
|
9e6b74e8ee | |
|
|
48a6d353b6 | |
|
|
fd136460b4 | |
|
|
290163871d | |
|
|
3bad2e95d2 | |
|
|
41f91e1de2 | |
|
|
8769c591bb | |
|
|
6b2949e834 | |
|
|
ce3e5eb69e | |
|
|
078f8a701e | |
|
|
a1f8601481 | |
|
|
c523058923 | |
|
|
55463407e0 | |
|
|
882b07dd41 | |
|
|
787b35c979 | |
|
|
94b5acd6db | |
|
|
e970820b9b | |
|
|
48f25a825b | |
|
|
9f89678c45 | |
|
|
032e496a26 | |
|
|
630a4552d6 | |
|
|
6c98203b45 | |
|
|
382c82513f | |
|
|
3ff21b70ed | |
|
|
925fa80473 | |
|
|
db0752a530 | |
|
|
de412d36d1 | |
|
|
7763f6646c | |
|
|
a93236ade6 | |
|
|
ada7f1d27d | |
|
|
0eef0a12f3 | |
|
|
31be601d72 | |
|
|
2db0d6b20d | |
|
|
1f404c5306 | |
|
|
605a501496 | |
|
|
17b714cfe7 | |
|
|
2aba6bfba5 | |
|
|
a9f3aa3f74 | |
|
|
01e9b00a36 | |
|
|
8a5d18dbb1 | |
|
|
976a5b441f | |
|
|
bbf477acb5 | |
|
|
6301abfe6a | |
|
|
f56e03cf0b | |
|
|
f6c9223f65 | |
|
|
09a8fc5a4f | |
|
|
8e5a2be92a | |
|
|
71cf47a76e | |
|
|
295bc67569 | |
|
|
4f47bd68c6 | |
|
|
946ff4a6f9 | |
|
|
aa41a5fed1 | |
|
|
f4facd0f69 | |
|
|
da0834e46e | |
|
|
da70913280 | |
|
|
c7752dc1be | |
|
|
22b771615f | |
|
|
0bd03f5140 | |
|
|
977e98c2b4 | |
|
|
1bf138ada9 | |
|
|
bcaa6f18bd | |
|
|
3b5d167662 | |
|
|
a311812cf3 | |
|
|
eeaf8f620f | |
|
|
8a169467c0 | |
|
|
ad148795a8 | |
|
|
191d961f84 | |
|
|
d64fdb3416 | |
|
|
2a0682ea22 | |
|
|
90d4f7528b | |
|
|
cfadc7403a | |
|
|
27c57401e9 | |
|
|
3662843f46 | |
|
|
e29f61d9b6 | |
|
|
01afad7c4f | |
|
|
a294e5cc7b | |
|
|
82e60f9db7 | |
|
|
438f0325f6 | |
|
|
72a2db4f3a | |
|
|
85bf3bc33a | |
|
|
92f7417ffe | |
|
|
619403dbc6 | |
|
|
272d9db20c | |
|
|
e41998b0c5 | |
|
|
989157a852 | |
|
|
5b6e3ec8a8 | |
|
|
086134a8ed | |
|
|
b59541f6e2 | |
|
|
e3cde4f4a0 | |
|
|
33b5401337 | |
|
|
68daa42635 | |
|
|
ca1754e3f0 | |
|
|
ff2ecbeefc | |
|
|
68c4b89344 | |
|
|
25cd597ae6 | |
|
|
a11264eb07 | |
|
|
5be2bf0247 | |
|
|
6b20fa8ea5 | |
|
|
99aeeea4bd | |
|
|
396379e571 | |
|
|
459038157d | |
|
|
0d5753a208 | |
|
|
8f30d22e78 | |
|
|
452a7e83e9 | |
|
|
88933c145d | |
|
|
a4dbb6eaed | |
|
|
047628c7bb | |
|
|
1925fe1cc1 | |
|
|
4553d42d99 | |
|
|
6a5ad5e30c | |
|
|
745374a55d | |
|
|
06f548d89b | |
|
|
d3cc8c5fd2 | |
|
|
cd9c6a232f | |
|
|
c91d70bc8a | |
|
|
423a3d1862 | |
|
|
b07d368154 | |
|
|
7b3c2b9c08 | |
|
|
220594da3e | |
|
|
58d107fade | |
|
|
223c43a656 | |
|
|
8c5f60089f | |
|
|
105ea7483d | |
|
|
1401bbf07f | |
|
|
3809a09f44 | |
|
|
5f6a6d14be | |
|
|
792feb5acd | |
|
|
f0ed354efc | |
|
|
00dbdd0493 | |
|
|
7dff44fcd1 | |
|
|
c5ef8cb59f | |
|
|
b0e2acf9b4 | |
|
|
59f4e60efd | |
|
|
440d5a8911 | |
|
|
2bac7cdedf | |
|
|
68a38cdb38 | |
|
|
55aee25982 | |
|
|
42a916ac7b | |
|
|
3fd5670471 | |
|
|
dfee45b866 | |
|
|
e5447fc5a1 | |
|
|
0b895730e8 | |
|
|
c9e5dd4dc0 | |
|
|
9f603ce26d | |
|
|
5b75f47b69 | |
|
|
d3023a6578 | |
|
|
5967ea8b8c | |
|
|
1cf1520819 | |
|
|
14bf640945 | |
|
|
2943ea0a5b | |
|
|
a7f5c941e2 | |
|
|
d7354a9958 | |
|
|
6b9a902065 | |
|
|
380c37d0cb | |
|
|
25dbd27802 | |
|
|
e3906cc815 | |
|
|
a66f81807a | |
|
|
8fd0f3aff7 | |
|
|
6de0859e7f | |
|
|
022b5883f3 | |
|
|
a02de0a4b1 | |
|
|
50d95cd0b3 | |
|
|
445f1a5cc2 | |
|
|
8d2ba6e0f0 | |
|
|
8bf5603390 | |
|
|
2d24155a4c | |
|
|
91d0696a35 | |
|
|
91496bac29 | |
|
|
2843dc080d | |
|
|
8781091930 | |
|
|
0e1fe96777 | |
|
|
ee4afb0c8f | |
|
|
fb60f0cce1 | |
|
|
72d562fb6f | |
|
|
a7df1822f0 | |
|
|
a426ce7868 | |
|
|
0fce97bcb8 | |
|
|
1069d02f16 | |
|
|
7826563de1 | |
|
|
3e0a943c04 | |
|
|
9aa038a3ce | |
|
|
aea86cb426 | |
|
|
c23db5fa16 | |
|
|
25e5abd622 | |
|
|
0def73e0da | |
|
|
3444e1c5d2 | |
|
|
61fdb1e569 | |
|
|
2cfc1e6126 | |
|
|
1d2dfc3a01 | |
|
|
e570a29644 | |
|
|
0cdf6d28d6 | |
|
|
5551ed5d3d | |
|
|
342840070d | |
|
|
b8c679f295 | |
|
|
9a221ab044 | |
|
|
094c0b8e00 | |
|
|
13929bb759 | |
|
|
0f88c23b82 | |
|
|
4ce5d1313b | |
|
|
56eb23e442 | |
|
|
5c268a909e | |
|
|
01a707aecc | |
|
|
144b33d23d | |
|
|
07fa228f13 | |
|
|
26bd5316ca | |
|
|
0858c0ddbc | |
|
|
bd6be5e1e8 | |
|
|
4e7bde56b1 | |
|
|
cc4ead08db | |
|
|
cc5d7884c8 | |
|
|
b270caa182 | |
|
|
9f899b8ff4 | |
|
|
678cf2ff6a | |
|
|
eb80fa78ff | |
|
|
2cc8809b50 | |
|
|
feb313b165 | |
|
|
a345c30643 | |
|
|
153c965110 | |
|
|
60c218473a | |
|
|
866327a941 | |
|
|
771c7df4d6 | |
|
|
7085d351e6 | |
|
|
0a43511c0e | |
|
|
84f31c12d8 | |
|
|
47937673b4 | |
|
|
1108b5c528 | |
|
|
f23f4bb037 | |
|
|
3e086bb42c | |
|
|
764548f68b | |
|
|
db69e39fdc | |
|
|
50727ca14e | |
|
|
8365ff3c2e | |
|
|
c3071314ca | |
|
|
c202b77e22 | |
|
|
214b6091e6 | |
|
|
40080509cf | |
|
|
5ae4f2ed3f | |
|
|
e3bc3d66d8 | |
|
|
edd0f27ee3 | |
|
|
239b2fa129 | |
|
|
be0fd5f6ff | |
|
|
e5ef8005fd | |
|
|
b9cfc83038 | |
|
|
f5a491c585 | |
|
|
d6e1ad467d | |
|
|
c19df40864 | |
|
|
1483f5181f | |
|
|
f054ef4159 | |
|
|
40f1c63a4b | |
|
|
788ad4d873 | |
|
|
c5522f6fa1 | |
|
|
a30b778958 | |
|
|
8d4cbed041 | |
|
|
54cfa351fb | |
|
|
0f7daa7402 | |
|
|
8c14aa180f | |
|
|
a9a9527f45 | |
|
|
041dd2937e | |
|
|
f032305bc7 | |
|
|
e10562ce57 | |
|
|
9824385138 | |
|
|
74508f6e98 | |
|
|
ee3bfa5128 | |
|
|
d4ded699da | |
|
|
19ce8a7a83 | |
|
|
2e77b579d7 | |
|
|
bb00c0bdee | |
|
|
470c38cea4 | |
|
|
cdb2ed4561 | |
|
|
528d97ed9d | |
|
|
6453ce95a7 | |
|
|
c5f1e19a07 | |
|
|
7a88c3c473 | |
|
|
9fe5ea1240 | |
|
|
9177a5f211 | |
|
|
cf7f548a2b | |
|
|
3e5d3b4673 | |
|
|
2bcf5cb0a7 | |
|
|
d182391299 | |
|
|
b7870d5e0b | |
|
|
b983a94bbc | |
|
|
5f7e292f98 | |
|
|
2cdf7cbd19 | |
|
|
8674a2b138 | |
|
|
4e3ab0ed43 | |
|
|
ac5c287dc5 | |
|
|
190e2bbe1e | |
|
|
67207f9e0a | |
|
|
2816eb3516 | |
|
|
6b2879b69f | |
|
|
b4a7ace304 | |
|
|
308fe8ac66 | |
|
|
9211db7941 | |
|
|
3e2234c3f0 | |
|
|
f7fb8d1fbd | |
|
|
3c3709a913 | |
|
|
afa1701241 | |
|
|
4dec966f54 | |
|
|
24caffac16 | |
|
|
0f67dd6234 | |
|
|
e02cce093a | |
|
|
8e2a449a43 | |
|
|
d29c585139 | |
|
|
3a9e801057 | |
|
|
7a5b17b3fe | |
|
|
7a007863eb | |
|
|
0644eba34b | |
|
|
ea383c96e0 | |
|
|
a407fcb30a | |
|
|
3b7781f8e2 | |
|
|
8415d161e2 | |
|
|
412bcbea0a | |
|
|
b1f7cc4562 | |
|
|
cc001735e0 | |
|
|
0a93475e54 | |
|
|
ad3c25f254 | |
|
|
f381ead422 | |
|
|
584ae28122 | |
|
|
65af3502ad | |
|
|
a8c391a20c | |
|
|
1c2834dd0e | |
|
|
12a5463d21 | |
|
|
df43a58ad6 | |
|
|
924fc73add | |
|
|
a5398e34f7 | |
|
|
94965d7bfe | |
|
|
140231fe69 | |
|
|
d5fc272f1f | |
|
|
f547f68c27 | |
|
|
c8ae77e656 | |
|
|
026510aadb | |
|
|
717bd77313 | |
|
|
3e7d93d5c3 | |
|
|
24f1af934a | |
|
|
7ab78e9967 | |
|
|
83642ae13d | |
|
|
c2a9d8fdf4 | |
|
|
21edccdd55 | |
|
|
daf80eed19 | |
|
|
83e200663c | |
|
|
3bbc000a81 | |
|
|
661bb83a50 | |
|
|
2bf43c3602 | |
|
|
148bca1c7d | |
|
|
3a0f76e1bf | |
|
|
4adb6897c8 | |
|
|
6d3b40e607 | |
|
|
db2065a46e | |
|
|
b82d6e0612 | |
|
|
59d900d531 | |
|
|
8de86ed278 | |
|
|
025512eb19 | |
|
|
4ae6008980 | |
|
|
d732b00e09 | |
|
|
76249c049b | |
|
|
d4dc8cb72e | |
|
|
81d2d63080 | |
|
|
43e229bdf4 | |
|
|
a51086fb52 | |
|
|
588079abee | |
|
|
f172ebd53f | |
|
|
a178c1bc62 | |
|
|
cce23dc2d7 | |
|
|
856808c671 | |
|
|
4702433944 | |
|
|
b8afbee410 | |
|
|
7ec22e9521 | |
|
|
eadbef6314 | |
|
|
f840712253 | |
|
|
2db4e11637 | |
|
|
e84976dc13 | |
|
|
ddd2ab9a25 | |
|
|
6e407159a3 | |
|
|
d7136656b8 | |
|
|
9e826a05a7 | |
|
|
9b6dfe15de | |
|
|
639dd9f5c7 | |
|
|
03f27fb433 | |
|
|
61818463df | |
|
|
c4c5c7c460 | |
|
|
7982fbb987 | |
|
|
868c6254bb | |
|
|
e225de096e | |
|
|
33797895e2 | |
|
|
485dbb086d | |
|
|
fa2c2efc67 | |
|
|
5293a4f640 | |
|
|
28c053e2e5 | |
|
|
dbe3a190c3 | |
|
|
b93adb4a44 | |
|
|
6cea5478cb | |
|
|
4192a76350 | |
|
|
5872baead0 | |
|
|
85341ddca4 | |
|
|
8c23ca2cdb | |
|
|
778e8abdc7 | |
|
|
f296303b19 | |
|
|
ec15bcd86d | |
|
|
7dccb854d6 | |
|
|
b401f81a84 | |
|
|
0f0cfb0ad3 | |
|
|
d816cb744e | |
|
|
136f7bfb41 | |
|
|
2110401d9b | |
|
|
dc1a5ead13 | |
|
|
c69ced1b6c | |
|
|
1ec23a4f33 | |
|
|
3218bf20b1 | |
|
|
7900ef07dc | |
|
|
e9516f5311 | |
|
|
bba4ccf0bc | |
|
|
4099884230 | |
|
|
020085f2e5 | |
|
|
aedfb9c0e3 | |
|
|
ed1a4cf63e | |
|
|
24d256a6c6 | |
|
|
5d5f72bba8 | |
|
|
b76bbdc0e1 | |
|
|
ace77fdc7e | |
|
|
4a27ff7dec | |
|
|
3a86b82cf7 | |
|
|
5a831250ca | |
|
|
631ba46d9d | |
|
|
80b5a146ab | |
|
|
cc090a5fe3 | |
|
|
d7141d287b | |
|
|
98cadeb8a5 | |
|
|
a4bc07a717 | |
|
|
d218a71471 | |
|
|
b917e81634 | |
|
|
76ede2c74b | |
|
|
fb2ae3f7f9 | |
|
|
f3a0698a4d | |
|
|
48dc96139b | |
|
|
918628cb41 | |
|
|
22921eb3e2 | |
|
|
1ff9e6ecef | |
|
|
e0030d53d4 | |
|
|
9210c446b2 | |
|
|
25ba5e7ea4 | |
|
|
27a221c95c | |
|
|
370233e1b0 | |
|
|
9315ab7bcb | |
|
|
9a9bdeadd4 | |
|
|
ccaecc9fe7 | |
|
|
1095ba8e73 | |
|
|
e9c23f28ba | |
|
|
98f6553b3b | |
|
|
f4ad73518b | |
|
|
a5ace97cb4 | |
|
|
f2835f7d5a | |
|
|
c8e150c57f | |
|
|
62e3de61d5 | |
|
|
ab58636fdc |
|
|
@ -0,0 +1,80 @@
|
||||||
|
name: Spider Jar Gen CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ "multiThread" ]
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Set up JDK 21
|
||||||
|
uses: actions/setup-java@v5
|
||||||
|
with:
|
||||||
|
java-version: '21'
|
||||||
|
distribution: 'jetbrains'
|
||||||
|
cache: gradle
|
||||||
|
|
||||||
|
- name: Grant execute permission to gradlew
|
||||||
|
run: chmod +x ./gradlew
|
||||||
|
|
||||||
|
- name: Build with Gradle
|
||||||
|
run: ./gradlew app:buildCustomSpiderJar
|
||||||
|
|
||||||
|
|
||||||
|
- name: Add & Commit
|
||||||
|
uses: EndBug/add-and-commit@v9.1.4
|
||||||
|
with:
|
||||||
|
default_author: github_actions
|
||||||
|
message: 'update spider jar and json'
|
||||||
|
add: "['./jar/custom_spider.jar', './jar/custom_spider.jar.md5','./json/index.json']"
|
||||||
|
|
||||||
|
- name: upload json
|
||||||
|
uses: SamKirkland/FTP-Deploy-Action@v4.3.6
|
||||||
|
with:
|
||||||
|
# ftp server
|
||||||
|
server: ftpupload.net
|
||||||
|
# ftp username
|
||||||
|
username: mseet_40633048
|
||||||
|
# ftp password
|
||||||
|
password: ${{ secrets.FTP_PWD }}
|
||||||
|
# Server port to connect to (read your web hosts docs)
|
||||||
|
port: 21
|
||||||
|
# protocol to deploy with - ftp, ftps, or ftps-legacy
|
||||||
|
protocol: ftp
|
||||||
|
# Folder to upload from, must end with trailing slash /
|
||||||
|
local-dir: ${{ github.workspace }}/json/
|
||||||
|
# Path to upload to on the server. Must end with trailing slash /
|
||||||
|
server-dir: htdocs/json/
|
||||||
|
# Deletes ALL contents of server-dir, even items in excluded with exclude argument
|
||||||
|
dangerous-clean-slate: true
|
||||||
|
exclude: |
|
||||||
|
**/js/**
|
||||||
|
|
||||||
|
- name: upload jar
|
||||||
|
uses: SamKirkland/FTP-Deploy-Action@v4.3.6
|
||||||
|
with:
|
||||||
|
# ftp server
|
||||||
|
server: ftpupload.net
|
||||||
|
# ftp username
|
||||||
|
username: mseet_40633048
|
||||||
|
# ftp password
|
||||||
|
password: ${{ secrets.FTP_PWD }}
|
||||||
|
# Server port to connect to (read your web hosts docs)
|
||||||
|
port: 21
|
||||||
|
# protocol to deploy with - ftp, ftps, or ftps-legacy
|
||||||
|
protocol: ftp
|
||||||
|
# Folder to upload from, must end with trailing slash /
|
||||||
|
local-dir: ${{ github.workspace }}/jar/
|
||||||
|
# Path to upload to on the server. Must end with trailing slash /
|
||||||
|
server-dir: htdocs/jar/
|
||||||
|
# Deletes ALL contents of server-dir, even items in excluded with exclude argument
|
||||||
|
dangerous-clean-slate: true
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
kotlin version: 2.0.10
|
||||||
|
error message: The daemon has terminated unexpectedly on startup attempt #1 with error code: 0. The daemon process output:
|
||||||
|
1. Kotlin compile daemon is ready
|
||||||
|
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
kotlin version: 2.0.10
|
||||||
|
error message: The daemon has terminated unexpectedly on startup attempt #1 with error code: 0. The daemon process output:
|
||||||
|
1. Kotlin compile daemon is ready
|
||||||
|
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
kotlin version: 2.0.10
|
||||||
|
error message: The daemon has terminated unexpectedly on startup attempt #1 with error code: 0. The daemon process output:
|
||||||
|
1. Kotlin compile daemon is ready
|
||||||
|
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
kotlin version: 2.0.10
|
||||||
|
error message: The daemon has terminated unexpectedly on startup attempt #1 with error code: 0. The daemon process output:
|
||||||
|
1. Kotlin compile daemon is ready
|
||||||
|
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
kotlin version: 2.0.10
|
||||||
|
error message: The daemon has terminated unexpectedly on startup attempt #1 with error code: 0. The daemon process output:
|
||||||
|
1. Kotlin compile daemon is ready
|
||||||
|
|
||||||
|
|
@ -0,0 +1,212 @@
|
||||||
|
# TVbox源贡献指南
|
||||||
|
|
||||||
|
|
||||||
|
### 欢迎各路大佬踊跃提PR,分享爬虫代码。
|
||||||
|
|
||||||
|
## 源码仓库地址
|
||||||
|
https://github.com/lushunming/AndroidCatVodSpider
|
||||||
|
|
||||||
|
## 快速开始
|
||||||
|
----
|
||||||
|
本工程是一个完整的AndroidStudio工程,请你用AS打开编辑。
|
||||||
|
|
||||||
|
工程调试完毕后要需要导出生成jar文件配合软件使用,执行根目录下的 `build.bat` 会在`jar`目录生成一个名为`custom_spider.jar`的jar文件,这个文件就是我们最终要是用的代码包。
|
||||||
|
|
||||||
|
### 代码包食用方式
|
||||||
|
----
|
||||||
|
本地加载:将`custom_spider.jar`放入设备sd卡根目录即可。 **注意,如需本地加载,请手动赋予App存储空间读写权限,App默认不申请存储空间读写权限**
|
||||||
|
|
||||||
|
远程加载:将`custom_spider.jar`上传到你的网络空间,获取对应的文件下载地址,在软件自定义配置的json文件中加入下面格式的键值对。
|
||||||
|
```json
|
||||||
|
"spider": "http://xxx.xxx.xxx/custom_spider.jar"
|
||||||
|
```
|
||||||
|
支持jar文件本地缓存(需v2.0.5及以上版本)
|
||||||
|
```json
|
||||||
|
"spider": "http://xxx.xxx.xxx/custom_spider.jar;md5;jar文件md5"
|
||||||
|
// 例如
|
||||||
|
"spider": "https://github.com/catvod/CatVodTVSpider/blob/master/jar/custom_spider.jar?raw=true;md5;c6ed6bc8285f0aca90e7cb3abf7f9caa",
|
||||||
|
```
|
||||||
|
|
||||||
|
### 如何在自定义配置中调用我们代码包中的Spider
|
||||||
|
----
|
||||||
|
同样在自定义json中加入相应的播放源即可,**type=3, api对应你代码工程中自定义的爬虫类名(api必须是`csp_`开头),例如实例工程中的`Aidi`**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"key": "csp_Aidi",
|
||||||
|
"name": "爱迪",
|
||||||
|
"type": 3,
|
||||||
|
"api": "csp_Aidi",
|
||||||
|
"searchable": 1,
|
||||||
|
"quickSearch": 0,
|
||||||
|
"filterable": 1
|
||||||
|
}
|
||||||
|
```
|
||||||
|
---
|
||||||
|
**Json解析扩展(需v2.0.2及以上版本)**
|
||||||
|
|
||||||
|
通过jar包可以实现json解析并发、轮询等相关功能,**参与并发和轮询的json解析地址,默认为解析地址列表中的所有json解析(即type=1)**。
|
||||||
|
|
||||||
|
在自定义json中的`parse`里加入相应的解析配置(type=2)即可启用。**调用扩展类的名称配置在`parse`的`url`字段里,例如扩展类`JsonParallel`的json配置`url`字段值为`Parallel`**。如下:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "Json并发",
|
||||||
|
"type": 2,
|
||||||
|
"url": "Parallel"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Json轮询",
|
||||||
|
"type": 2,
|
||||||
|
"url": "Sequence"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**部分内置代理接口使用(需v2.0.9及以上版本)**
|
||||||
|
|
||||||
|
入口 [Proxy](/app/src/main/java/com/github/catvod/spider/Proxy.java)
|
||||||
|
|
||||||
|
代理,示例参考[Ddrk](/app/src/main/java/com/github/catvod/spider/Ddrk.java)
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## 基础类
|
||||||
|
----
|
||||||
|
|
||||||
|
> com.github.catvod.spider.Spider 爬虫基类
|
||||||
|
> com.github.catvod.spider.Cloud 网盘(UC、ali、quark)爬虫开发类
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## 示例
|
||||||
|
----
|
||||||
|
|
||||||
|
请查看 仓库中的Ddrk 相关实现 ,调试可参考 `com.github.catvod.demo.MainActivity` ,直接调用对应爬虫相关接口
|
||||||
|
|
||||||
|
> com.github.catvod.spider.Ddrk 爬虫类
|
||||||
|
> com.github.catvod.spider.DdrkTest 测试类
|
||||||
|
|
||||||
|
## 注意事项!!
|
||||||
|
----
|
||||||
|
1. 除了`com.github.catvod.spider`包以外的代码,最终都会被软件本身内置的代码代替掉,所以,建议你不要修改除`com.github.catvod.spider`包以外的代码。
|
||||||
|
|
||||||
|
2. 需要提交爬虫和对应的爬虫测试类,不然不接受PR
|
||||||
|
|
||||||
|
3. 待补充
|
||||||
|
|
||||||
|
## 爬虫类返回的相关Json字符串格式说明
|
||||||
|
----
|
||||||
|
### homeContent
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"class": [{ // 分类
|
||||||
|
"type_id": "dianying", // 分类id
|
||||||
|
"type_name": "电影" // 分类名
|
||||||
|
}, {
|
||||||
|
"type_id": "lianxuju",
|
||||||
|
"type_name": "连续剧"
|
||||||
|
}],
|
||||||
|
"filters": { // 筛选
|
||||||
|
"dianying": [{ // 分类id 就是上面class中的分类id
|
||||||
|
"key": "0", // 筛选key
|
||||||
|
"name": "分类", // 筛选名称
|
||||||
|
"value": [{ // 筛选选项
|
||||||
|
"n": "全部", // 选项展示的名称
|
||||||
|
"v": "dianying" // 选项最终在url中的展现
|
||||||
|
}, {
|
||||||
|
"n": "动作片",
|
||||||
|
"v": "dongzuopian"
|
||||||
|
}]
|
||||||
|
}],
|
||||||
|
"lianxuju": [{
|
||||||
|
"key": 0,
|
||||||
|
"name": "分类",
|
||||||
|
"value": [{
|
||||||
|
"n": "全部",
|
||||||
|
"v": "lianxuju"
|
||||||
|
}, {
|
||||||
|
"n": "国产剧",
|
||||||
|
"v": "guochanju"
|
||||||
|
}, {
|
||||||
|
"n": "港台剧",
|
||||||
|
"v": "gangtaiju"
|
||||||
|
}]
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
"list": [{ // 首页最近更新视频列表
|
||||||
|
"vod_id": "1901", // 视频id
|
||||||
|
"vod_name": "判决", // 视频名
|
||||||
|
"vod_pic": "https:\/\/pic.imgdb.cn\/item\/614631e62ab3f51d918e9201.jpg", // 展示图片
|
||||||
|
"vod_remarks": "6.8" // 视频信息 展示在 视频名上方
|
||||||
|
}, {
|
||||||
|
"vod_id": "1908",
|
||||||
|
"vod_name": "移山的父亲",
|
||||||
|
"vod_pic": "https:\/\/pic.imgdb.cn\/item\/6146fab82ab3f51d91c01af1.jpg",
|
||||||
|
"vod_remarks": "6.7"
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### categoryContent
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"page": 1, // 当前页
|
||||||
|
"pagecount": 2, // 总共几页
|
||||||
|
"limit": 60, // 每页几条数据
|
||||||
|
"total": 120, // 总共多少调数据
|
||||||
|
"list": [{ // 视频列表 下面的视频结构 同上面homeContent中的
|
||||||
|
"vod_id": "1897",
|
||||||
|
"vod_name": "北区侦缉队",
|
||||||
|
"vod_pic": "https:\/\/pic.imgdb.cn\/item\/6145d4b22ab3f51d91bd98b6.jpg",
|
||||||
|
"vod_remarks": "7.3"
|
||||||
|
}, {
|
||||||
|
"vod_id": "1879",
|
||||||
|
"vod_name": "浪客剑心 最终章 人诛篇",
|
||||||
|
"vod_pic": "https:\/\/pic.imgdb.cn\/item\/60e3f37e5132923bf82ef95e.jpg",
|
||||||
|
"vod_remarks": "8.0"
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### detailContent
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"list": [{
|
||||||
|
"vod_id": "1902",
|
||||||
|
"vod_name": "海岸村恰恰恰",
|
||||||
|
"vod_pic": "https:\/\/pic.imgdb.cn\/item\/61463fd12ab3f51d91a0f44d.jpg",
|
||||||
|
"type_name": "剧情",
|
||||||
|
"vod_year": "2021",
|
||||||
|
"vod_area": "韩国",
|
||||||
|
"vod_remarks": "更新至第8集",
|
||||||
|
"vod_actor": "申敏儿,金宣虎,李相二,孔敏晶,徐尚沅,禹美华,朴艺荣,李世亨,边胜泰,金贤佑,金英玉",
|
||||||
|
"vod_director": "柳济元",
|
||||||
|
"vod_content": "海岸村恰恰恰剧情: 韩剧海岸村恰恰恰 갯마을 차차차改编自2004年的电影《我的百事通男友洪班长》,海岸村恰恰恰 갯마을 차차차讲述来自大都市的牙医(申敏儿 饰)到充满人情味的海岸村开设牙医诊所,那里住着一位各方面都",
|
||||||
|
// 播放源 多个用$$$分隔
|
||||||
|
"vod_play_from": "qiepian$$$yun3edu",
|
||||||
|
// 播放列表 注意分隔符 分别是 多个源$$$分隔,源中的剧集用#分隔,剧集的名称和地址用$分隔
|
||||||
|
"vod_play_url": "第1集$1902-1-1#第2集$1902-1-2#第3集$1902-1-3#第4集$1902-1-4#第5集$1902-1-5#第6集$1902-1-6#第7集$1902-1-7#第8集$1902-1-8$$$第1集$1902-2-1#第2集$1902-2-2#第3集$1902-2-3#第4集$1902-2-4#第5集$1902-2-5#第6集$1902-2-6#第7集$1902-2-7#第8集$1902-2-8"
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### searchContent
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"list": [{ // 视频列表 下面的视频结构 同上面homeContent中的
|
||||||
|
"vod_id": "1606",
|
||||||
|
"vod_name": "陪你一起长大",
|
||||||
|
"vod_pic": "https:\/\/img.aidi.tv\/img\/upload\/vod\/20210417-1\/e27d4eb86f7cde375171dd324b2c19ae.jpg",
|
||||||
|
"vod_remarks": "更新至第37集"
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2024 lushunming
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
40
README.md
40
README.md
|
|
@ -1,5 +1,39 @@
|
||||||
# CatVodSpider
|
# CatVodSpider for TVBOX android
|
||||||
|
|
||||||
### Based on CatVod
|
## 提示
|
||||||
|
本项目不提供网盘账号,需要自己登录网盘账号,如果无法使用确保自己的网盘是有空间的,需要高清视频需要自己开通网盘会员,谢谢!!!
|
||||||
|
|
||||||
https://github.com/CatVodTVOfficial/CatVodTVSpider
|
## Based on CatVod
|
||||||
|
|
||||||
|
fork from https://github.com/FongMi/CatVodSpider
|
||||||
|
|
||||||
|
spider 参考 https://github.com/bizhangjie/CatVodSpider
|
||||||
|
|
||||||
|
jsSpider 参考 https://github.com/jadehh/TVSpider
|
||||||
|
|
||||||
|
源链接:https://mirror.ghproxy.com/https://raw.githubusercontent.com/lushunming/AndroidCatVodSpider/mine/json/index.json
|
||||||
|
或者 https://androidcatvodspider.netlify.app/json/index.json
|
||||||
|
|
||||||
|
|
||||||
|
## 贡献
|
||||||
|
请参考 [CONTRIBUTING.md](/CONTRIBUTING.md)
|
||||||
|
|
||||||
|
## 教程
|
||||||
|
请参考 [USE.md](/USE.md)
|
||||||
|
|
||||||
|
## ✨ Star 数
|
||||||
|
|
||||||
|
[](https://star-history.com/#lushunming/AndroidCatVodSpider&Date)
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
## 公众号
|
||||||
|
关注公众号,获取最新更新。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 备注
|
||||||
|
该项目仅为自己自用开发,如果发现bug,可以提issues,但是基本不接受新需求(除非我感兴趣的新需求)。
|
||||||
|
|
||||||
|
## 群组
|
||||||
|
为了方便讨论可以加群组,但不保证及时回复 https://t.me/+YLvmJsZoFmcyN2M1
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# TVBox源使用教程
|
||||||
|
|
||||||
|
昨天更新之后,很多人问如何使用,今天写一篇补充教程。
|
||||||
|
|
||||||
|
## 地址
|
||||||
|
https://androidcatvodspider.netlify.app/json/index.json
|
||||||
|
|
||||||
|
## 如何操作
|
||||||
|
|
||||||
|
1. 清除源缓存,更新到最新的源
|
||||||
|
2. 站源切换到`Cookie设置`
|
||||||
|
3. 选择分类`UC`,然后点击设置Token,弹出二维码后,用手机端扫码即可。
|
||||||
|
4. 如果以前使用过UC,那么就可以直接享受UC原画高清视频。如果是本源的新用户,需要找一个UC资源观看,他会自动弹出窗口,然后选择扫二维码扫码即可观看。
|
||||||
|
|
||||||
|
## 存在问题
|
||||||
|
1. 有的新用户一直提示扫码,可能是TVBOX的文件权限没开,无法在本地生成文件,打开文件权限就行。
|
||||||
|
2. 有的旧用户设置了Cookie还是无法观看,可以到文件管理里面找到TV文件夹,删除里面的`.uc` 和`.uctoken`文件(可能被隐藏,设置里打开显示隐藏文件,或者将文件夹删除),再重新按照操作走一遍。
|
||||||
184
app/build.gradle
184
app/build.gradle
|
|
@ -1,17 +1,26 @@
|
||||||
plugins {
|
plugins {
|
||||||
id 'com.android.application'
|
id 'com.android.application'
|
||||||
id 'ru.cleverpumpkin.proguard-dictionaries-generator'
|
id 'ru.cleverpumpkin.proguard-dictionaries-generator'
|
||||||
}
|
id 'org.jetbrains.kotlin.android'
|
||||||
|
|
||||||
|
}
|
||||||
|
java {
|
||||||
|
toolchain {
|
||||||
|
languageVersion = JavaLanguageVersion.of(17)
|
||||||
|
}
|
||||||
|
}
|
||||||
android {
|
android {
|
||||||
namespace 'com.github.catvod'
|
namespace 'com.github.catvod'
|
||||||
|
|
||||||
compileSdk 35
|
compileSdk 34
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "com.github.catvod.demo"
|
applicationId "com.github.catvod.demo"
|
||||||
minSdk 16
|
minSdk 16
|
||||||
targetSdk 35
|
targetSdk 34
|
||||||
|
ndk { abiFilters "arm64-v8a" }
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
|
|
@ -28,9 +37,23 @@ android {
|
||||||
}
|
}
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
sourceCompatibility JavaVersion.VERSION_1_8
|
sourceCompatibility JavaVersion.VERSION_11
|
||||||
targetCompatibility JavaVersion.VERSION_1_8
|
targetCompatibility JavaVersion.VERSION_11
|
||||||
}
|
}
|
||||||
|
testOptions {
|
||||||
|
unitTests.returnDefaultValues = true
|
||||||
|
unitTests.includeAndroidResources = true
|
||||||
|
}
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = "11"
|
||||||
|
|
||||||
|
}
|
||||||
|
packagingOptions {
|
||||||
|
|
||||||
|
exclude 'META-INF/*'
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
configurations.configureEach {
|
configurations.configureEach {
|
||||||
resolutionStrategy {
|
resolutionStrategy {
|
||||||
|
|
@ -41,11 +64,160 @@ android {
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'com.github.thegrizzlylabs:sardine-android:0.9'
|
implementation 'com.github.thegrizzlylabs:sardine-android:0.9'
|
||||||
implementation 'wang.harlon.quickjs:wrapper-android:0.21.1'
|
implementation 'wang.harlon.quickjs:wrapper-android:2.0.0'
|
||||||
implementation 'com.squareup.okhttp3:okhttp:3.12.13'
|
implementation 'com.squareup.okhttp3:okhttp:3.12.13'
|
||||||
implementation 'com.google.code.gson:gson:2.11.0'
|
implementation 'com.google.code.gson:gson:2.11.0'
|
||||||
implementation 'cn.wanghaomiao:JsoupXpath:2.5.1'
|
implementation 'cn.wanghaomiao:JsoupXpath:2.5.1'
|
||||||
implementation 'com.google.zxing:core:3.3.0'
|
implementation 'com.google.zxing:core:3.3.0'
|
||||||
implementation 'com.orhanobut:logger:2.2.0'
|
implementation 'com.orhanobut:logger:2.2.0'
|
||||||
implementation 'org.jsoup:jsoup:1.15.3'
|
implementation 'org.jsoup:jsoup:1.15.3'
|
||||||
|
implementation 'androidx.core:core-ktx:1.10.1'
|
||||||
|
testImplementation "io.github.dokar3:quickjs-kt-jvm:1.0.0-alpha13"
|
||||||
|
testImplementation 'org.nanohttpd:nanohttpd:2.3.1'
|
||||||
|
implementation 'commons-codec:commons-codec:1.17.1'
|
||||||
|
|
||||||
|
// Required -- JUnit 4 framework
|
||||||
|
testImplementation 'junit:junit:4.12'
|
||||||
|
// Optional -- Mockito framework(可选,用于模拟一些依赖对象,以达到隔离依赖的效果)
|
||||||
|
testImplementation 'org.mockito:mockito-core:5.12.0'
|
||||||
|
testImplementation "org.robolectric:robolectric:4.13"
|
||||||
|
testImplementation 'cn.hutool:hutool-all:5.8.26'
|
||||||
|
|
||||||
|
|
||||||
|
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.1"
|
||||||
|
// https://mvnrepository.com/artifact/com.sun.net.httpserver/http
|
||||||
|
/* implementation(files("libs/sun-common-server.jar"))
|
||||||
|
implementation("com.sun.net.httpserver:http:20070405")*/
|
||||||
|
// implementation("com.hibegin:simplewebserver:0.1.15")
|
||||||
|
//implementation("com.github.codeborne.klite:klite-server:1.7.0")
|
||||||
|
|
||||||
|
|
||||||
|
//implementation 'wang.harlon.quickjs:wrapper-java:1.0.0'
|
||||||
|
// implementation(ext: 'aar', name: 'quickjs', group: 'fongmi', version: 'release')
|
||||||
|
// api 'wang.harlon.quickjs:wrapper-android:2.0.0'
|
||||||
|
|
||||||
|
task buildCustomSpiderJar {
|
||||||
|
doLast {
|
||||||
|
def workDir = file("${projectDir}").parentFile
|
||||||
|
def spiderJarDir = file("$workDir/jar/spider.jar")
|
||||||
|
def smaliOutputDir = file("$workDir/jar/Smali_classes")
|
||||||
|
def customSpiderJar = file("$workDir/jar/custom_spider.jar")
|
||||||
|
def baksmaliJar = file("$workDir/jar/3rd/baksmali-2.5.2.jar")
|
||||||
|
def apktoolJar = file("$workDir/jar/3rd/apktool_2.4.1.jar")
|
||||||
|
def classesDex = file("$workDir/app/build/intermediates/dex/release/minifyReleaseWithR8/classes.dex")
|
||||||
|
def indexJsonFile = file("$workDir/json/index.json")
|
||||||
|
|
||||||
|
// 删除旧文件
|
||||||
|
if (customSpiderJar.exists()) {
|
||||||
|
customSpiderJar.delete()
|
||||||
|
}
|
||||||
|
if (smaliOutputDir.exists()) {
|
||||||
|
delete(smaliOutputDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用 baksmali 反编译 classes.dex
|
||||||
|
javaexec {
|
||||||
|
main = "-jar"
|
||||||
|
args = [
|
||||||
|
baksmaliJar.absolutePath,
|
||||||
|
"d",
|
||||||
|
classesDex.absolutePath,
|
||||||
|
"-o",
|
||||||
|
smaliOutputDir.absolutePath
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除 spider.jar 中的旧目录
|
||||||
|
def targetDirs = [
|
||||||
|
file("$spiderJarDir/smali/com/github/catvod/spider"),
|
||||||
|
file("$spiderJarDir/smali/com/github/catvod/parser"),
|
||||||
|
file("$spiderJarDir/smali/com/github/catvod/js")
|
||||||
|
]
|
||||||
|
targetDirs.each { dir ->
|
||||||
|
if (dir.exists()) {
|
||||||
|
delete(dir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建目标目录(如果不存在)
|
||||||
|
def targetBaseDir = file("$spiderJarDir/smali/com/github/catvod")
|
||||||
|
if (!targetBaseDir.exists()) {
|
||||||
|
targetBaseDir.mkdirs()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 移动反编译后的目录
|
||||||
|
def sourceDirs = [
|
||||||
|
file("$smaliOutputDir/com/github/catvod/spider"),
|
||||||
|
file("$smaliOutputDir/com/github/catvod/parser"),
|
||||||
|
file("$smaliOutputDir/com/github/catvod/js")
|
||||||
|
]
|
||||||
|
def targetPaths = [
|
||||||
|
file("$targetBaseDir/spider"),
|
||||||
|
file("$targetBaseDir/parser"),
|
||||||
|
file("$targetBaseDir/js")
|
||||||
|
]
|
||||||
|
|
||||||
|
sourceDirs.eachWithIndex { src, idx ->
|
||||||
|
if (src.exists()) {
|
||||||
|
copy {
|
||||||
|
from src
|
||||||
|
into targetPaths[idx]
|
||||||
|
}
|
||||||
|
delete(src)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用 apktool 打包
|
||||||
|
javaexec {
|
||||||
|
main = "-jar"
|
||||||
|
args = [
|
||||||
|
apktoolJar.absolutePath,
|
||||||
|
"b",
|
||||||
|
spiderJarDir.absolutePath,
|
||||||
|
"-c"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 移动生成的 dex.jar 到 custom_spider.jar
|
||||||
|
def distJar = file("$spiderJarDir/dist/dex.jar")
|
||||||
|
if (distJar.exists()) {
|
||||||
|
copy {
|
||||||
|
from distJar
|
||||||
|
into file("$workDir/jar")
|
||||||
|
rename "dex.jar", "custom_spider.jar"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成 MD5 校验文件
|
||||||
|
def md5File = file("$workDir/jar/custom_spider.jar.md5")
|
||||||
|
def md5 = java.security.MessageDigest.getInstance("MD5").digest(customSpiderJar.bytes).encodeHex().toString()
|
||||||
|
md5File.text = md5
|
||||||
|
|
||||||
|
// 替换index.json md5 内容
|
||||||
|
def indexJsonContent = indexJsonFile.text
|
||||||
|
|
||||||
|
// 使用正则表达式替换 spider 字段中的 MD5 值
|
||||||
|
def updatedContent = indexJsonContent.replaceAll(
|
||||||
|
/("spider":\s*"[^;]+;md5;)[^"]+"/,
|
||||||
|
'$1' + md5 + '"'
|
||||||
|
)
|
||||||
|
|
||||||
|
// 写回文件
|
||||||
|
indexJsonFile.write(updatedContent)
|
||||||
|
|
||||||
|
// 清理临时目录
|
||||||
|
delete(
|
||||||
|
"$spiderJarDir/build",
|
||||||
|
"$spiderJarDir/smali",
|
||||||
|
"$spiderJarDir/dist",
|
||||||
|
smaliOutputDir
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
tasks.named('buildCustomSpiderJar') {
|
||||||
|
dependsOn 'assembleRelease'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Binary file not shown.
|
|
@ -40,3 +40,16 @@
|
||||||
public static **[] values();
|
public static **[] values();
|
||||||
public static ** valueOf(java.lang.String);
|
public static ** valueOf(java.lang.String);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Please add these rules to your existing keep rules in order to suppress warnings.
|
||||||
|
# This is generated automatically by the Android Gradle plugin.
|
||||||
|
-dontwarn org.bouncycastle.jce.provider.BouncyCastleProvider
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-keepattributes SourceFile,LineNumberTable
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# 禁用代码混淆
|
||||||
|
#-dontobfuscate
|
||||||
|
|
@ -507,7 +507,7 @@ public class AliYun {
|
||||||
Map<String, String> headers = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
|
Map<String, String> headers = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
|
||||||
List<String> keys = Arrays.asList("referer", "icy-metadata", "range", "connection", "accept-encoding", "user-agent");
|
List<String> keys = Arrays.asList("referer", "icy-metadata", "range", "connection", "accept-encoding", "user-agent");
|
||||||
for (String key : params.keySet()) if (keys.contains(key)) headers.put(key, params.get(key));
|
for (String key : params.keySet()) if (keys.contains(key)) headers.put(key, params.get(key));
|
||||||
return ProxyVideo.proxy(downloadUrl, headers);
|
return ProxyVideo.proxyMultiThread(downloadUrl, headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getM3u8Url(String shareId, String fileId, String templateId) {
|
private String getM3u8Url(String shareId, String fileId, String templateId) {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,262 @@
|
||||||
|
package com.github.catvod.api
|
||||||
|
|
||||||
|
import android.app.AlertDialog
|
||||||
|
import android.content.DialogInterface
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.graphics.drawable.ColorDrawable
|
||||||
|
import android.text.TextUtils
|
||||||
|
import android.view.Gravity
|
||||||
|
import android.widget.FrameLayout
|
||||||
|
import android.widget.ImageView
|
||||||
|
import com.github.catvod.bean.BD.Cache
|
||||||
|
import com.github.catvod.bean.yun.User
|
||||||
|
import com.github.catvod.crawler.SpiderDebug
|
||||||
|
import com.github.catvod.net.OkHttp
|
||||||
|
import com.github.catvod.spider.Init
|
||||||
|
import com.github.catvod.utils.*
|
||||||
|
import okhttp3.Headers
|
||||||
|
import okhttp3.Request
|
||||||
|
import org.apache.commons.lang3.StringEscapeUtils
|
||||||
|
import java.io.File
|
||||||
|
import java.io.IOException
|
||||||
|
import java.io.UnsupportedEncodingException
|
||||||
|
import java.util.concurrent.Executors
|
||||||
|
import java.util.concurrent.ScheduledExecutorService
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
import kotlin.concurrent.Volatile
|
||||||
|
|
||||||
|
|
||||||
|
class BaiDuYunHandler private constructor() {
|
||||||
|
private val cache: Cache
|
||||||
|
private var service: ScheduledExecutorService? = null
|
||||||
|
private var dialog: AlertDialog? = null
|
||||||
|
private var cookies = ""
|
||||||
|
private val headers = mapOf(
|
||||||
|
"User-Agent" to "Mozilla/5.0 (Linux; Android 12; SM-X800) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.40 Safari/537.36",
|
||||||
|
"Accept" to "application/json, text/plain, */*",
|
||||||
|
"Content-Type" to "application/x-www-form-urlencoded",
|
||||||
|
"Origin" to "https://pan.baidu.com",
|
||||||
|
"Referer" to "https://pan.baidu.com/"
|
||||||
|
)
|
||||||
|
|
||||||
|
fun getCache(): File {
|
||||||
|
return Path.tv("bd")
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
cache = Cache.objectFrom(Path.read(getCache()))
|
||||||
|
}
|
||||||
|
|
||||||
|
private object Loader {
|
||||||
|
@Volatile
|
||||||
|
var INSTANCE: BaiDuYunHandler = BaiDuYunHandler()
|
||||||
|
}
|
||||||
|
|
||||||
|
val token: String
|
||||||
|
get() {
|
||||||
|
val user: User = cache.getUser()
|
||||||
|
return user.getCookie()
|
||||||
|
//return "cGM6MTg4OTY3ODE2MDE6eTM1Tjd1dG58MXxSQ1N8MTc1NDQ2OTgwNzEyOXxzMlN0T1VEV3lOVmF5V3pNbGFfM2tJbVp1ZmlqSHBqaEhTSzVyNHZqVXNRLmlhV3loSUxHNDFkMUI5N1BqXzhWN0dtVWtKLnBTclhpNGpZU1EuTGZWMTV3MVFoZmNpcEVoZkxUV2tvYjB0bkFTYV9RTUhhaHhveWx6YkdmcEhQdjNCS1lrbnp1LkxaWDdKOE40YkNNRjkzT3piNmx2Y0d3TWdVUkl5b18ubVUt";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@JvmStatic
|
||||||
|
fun get(): BaiDuYunHandler {
|
||||||
|
return Loader.INSTANCE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(java.lang.Exception::class)
|
||||||
|
fun startScan(): ByteArray {
|
||||||
|
val result = loginByQRCode()
|
||||||
|
// Step 2: Get QR Code
|
||||||
|
val byteStr: ByteArray = downloadQRCode(result["qrCodeImageUrl"]!!);
|
||||||
|
|
||||||
|
Init.run(Runnable { showQRCode(byteStr) })
|
||||||
|
// Step 3: Check login status
|
||||||
|
|
||||||
|
Init.execute(Runnable {
|
||||||
|
startService(
|
||||||
|
result["sign"]!!
|
||||||
|
)
|
||||||
|
})
|
||||||
|
return byteStr
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(IOException::class)
|
||||||
|
fun downloadQRCode(url: String): ByteArray {
|
||||||
|
val headers: MutableMap<String?, String?> = HashMap<String?, String?>()
|
||||||
|
headers.put(
|
||||||
|
"user-agent",
|
||||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
val request = Request.Builder().url(url).headers(Headers.of(headers)).build()
|
||||||
|
val response = OkHttp.newCall(request)
|
||||||
|
if (response.code() == 200) {
|
||||||
|
return response.body()!!.bytes()
|
||||||
|
}
|
||||||
|
return "".toByteArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun loginByQRCode(): Map<String, String> {
|
||||||
|
return try {
|
||||||
|
// 获取登录二维码
|
||||||
|
val timestamp = System.currentTimeMillis()
|
||||||
|
val qrCodeUrl = "https://passport.baidu.com/v2/api/getqrcode?lp=pc&_=$timestamp"
|
||||||
|
|
||||||
|
val response = OkHttp.string(qrCodeUrl, emptyMap(), headers)
|
||||||
|
val json = Json.safeObject(response)
|
||||||
|
|
||||||
|
if (json["errno"].asInt != 0) {
|
||||||
|
return mapOf("error" to "获取登录二维码错误, code: ${json["errno"].asInt}")
|
||||||
|
}
|
||||||
|
|
||||||
|
val sign = json["sign"].asString
|
||||||
|
val imgurl = json["imgurl"].asString
|
||||||
|
val qrCodeImageUrl = "https://$imgurl"
|
||||||
|
|
||||||
|
val qrLoginUrl =
|
||||||
|
"https://wappass.baidu.com/wp/?qrlogin&t=$timestamp" + "&error=0&sign=$sign&cmd=login&lp=pc&tpl=netdisk&uaonly=" + "&client_id=&adapter=3&client=&qrloginfrom=pc&wechat=0&traceid="
|
||||||
|
|
||||||
|
// 返回二维码信息供前端显示
|
||||||
|
val result = mapOf(
|
||||||
|
"qrCodeImageUrl" to qrCodeImageUrl, "qrLoginUrl" to qrLoginUrl, "sign" to sign
|
||||||
|
)
|
||||||
|
|
||||||
|
result
|
||||||
|
} catch (e: Exception) {
|
||||||
|
mapOf("error" to "获取二维码失败: ${e.message}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun checkQRLoginStatus(sign: String): Map<String, Any> {
|
||||||
|
return try {
|
||||||
|
val timestamp = System.currentTimeMillis()
|
||||||
|
val checkUrl =
|
||||||
|
"https://passport.baidu.com/channel/unicast?channel_id=$sign" + "&tpl=netdisk&callback=&apiver=v3&tt=$timestamp&_=$timestamp"
|
||||||
|
|
||||||
|
val response = OkHttp.string(checkUrl, emptyMap(), headers)
|
||||||
|
val cleanResponse = response.trim('(', ' ', '\n', ')')
|
||||||
|
val json = Json.safeObject(cleanResponse)
|
||||||
|
|
||||||
|
if (json["errno"].asInt == 0) {
|
||||||
|
SpiderDebug.log("百度扫码成功")
|
||||||
|
val channelV = json["channel_v"].asString
|
||||||
|
val channelJson = Json.safeObject(channelV)
|
||||||
|
|
||||||
|
if (channelJson["status"].asInt == 0) {
|
||||||
|
val bduss = channelJson["v"].asString
|
||||||
|
|
||||||
|
// 执行登录
|
||||||
|
val loginTimestamp = System.currentTimeMillis()
|
||||||
|
val loginUrl =
|
||||||
|
"https://passport.baidu.com/v3/login/main/qrbdusslogin?" + "v=$loginTimestamp&bduss=$bduss&u=&loginVersion=v4&qrcode=1&tpl=netdisk&apiver=v3" + "&tt=$loginTimestamp&traceid=&callback=bd__cbs__cupstt"
|
||||||
|
|
||||||
|
val loginResponse = OkHttp.get(loginUrl, emptyMap(), headers)
|
||||||
|
val cleanLoginResponse = loginResponse.body.substringAfter("(").substringBeforeLast(")")
|
||||||
|
val loginJson = Json.safeObject(StringEscapeUtils.unescapeHtml4(cleanLoginResponse))
|
||||||
|
|
||||||
|
if (loginJson.has("errInfo") && loginJson["errInfo"].asJsonObject["no"].asString == "0") {
|
||||||
|
// 登录成功,设置cookie
|
||||||
|
SpiderDebug.log("百度登录成功,设置cookie:${bduss}")
|
||||||
|
cookies = "BDUSS=$bduss"
|
||||||
|
cookies = generateCooike(loginResponse.resp["set-cookie"])
|
||||||
|
|
||||||
|
if (cookies.isNotEmpty()) {
|
||||||
|
cache.setUser(User.objectFrom(this.cookies))
|
||||||
|
//停止检验线程,关闭弹窗
|
||||||
|
stopService()
|
||||||
|
Notify.show("百度登录成功")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
mapOf("success" to true, "bduss" to bduss)
|
||||||
|
} else {
|
||||||
|
mapOf("error" to "登录失败: $cleanLoginResponse")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mapOf("status" to channelJson["status"].asInt)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mapOf("errno" to json["errno"].asInt)
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
mapOf("error" to "检查登录状态失败: ${e.message}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun generateCooike(cookies: List<String>?): String {
|
||||||
|
if (cookies == null || cookies.isEmpty()) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
val cookieList: MutableList<String?> = ArrayList<String?>()
|
||||||
|
for (cookie in cookies) {
|
||||||
|
cookieList.add(cookie.split(";".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()[0])
|
||||||
|
}
|
||||||
|
return TextUtils.join(";", cookieList)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 显示qrcode
|
||||||
|
*
|
||||||
|
* @param base64Str
|
||||||
|
*/
|
||||||
|
fun showQRCode(bytes: ByteArray) {
|
||||||
|
try {
|
||||||
|
val size = ResUtil.dp2px(240)
|
||||||
|
val params = FrameLayout.LayoutParams(size, size)
|
||||||
|
val image = ImageView(Init.context())
|
||||||
|
image.setScaleType(ImageView.ScaleType.CENTER_CROP)
|
||||||
|
image.setImageBitmap(QRCode.Bytes2Bimap(bytes))
|
||||||
|
val frame = FrameLayout(Init.context())
|
||||||
|
params.gravity = Gravity.CENTER
|
||||||
|
frame.addView(image, params)
|
||||||
|
dialog = AlertDialog.Builder(Init.getActivity()).setView(frame)
|
||||||
|
.setOnCancelListener(DialogInterface.OnCancelListener { dialog: DialogInterface? -> this.dismiss(dialog) })
|
||||||
|
.setOnDismissListener(DialogInterface.OnDismissListener { dialog: DialogInterface? ->
|
||||||
|
this.dismiss(dialog)
|
||||||
|
}).show()
|
||||||
|
dialog!!.getWindow()!!.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
|
||||||
|
Notify.show("请使用百度网盘App扫描二维码")
|
||||||
|
} catch (ignored: java.lang.Exception) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun dismiss() {
|
||||||
|
try {
|
||||||
|
dialog?.dismiss()
|
||||||
|
} catch (ignored: java.lang.Exception) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun dismiss(dialog: DialogInterface?) {
|
||||||
|
stopService()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun stopService() {
|
||||||
|
service?.shutdownNow()
|
||||||
|
Init.run(Runnable { this.dismiss() })
|
||||||
|
}
|
||||||
|
|
||||||
|
fun startService(sign: String) {
|
||||||
|
SpiderDebug.log("----start 百度 token service")
|
||||||
|
|
||||||
|
service = Executors.newScheduledThreadPool(1)
|
||||||
|
|
||||||
|
service?.scheduleWithFixedDelay(Runnable {
|
||||||
|
try {
|
||||||
|
SpiderDebug.log("----check百度tatus中")
|
||||||
|
|
||||||
|
checkQRLoginStatus(sign)
|
||||||
|
} catch (e: UnsupportedEncodingException) {
|
||||||
|
throw RuntimeException(e)
|
||||||
|
}
|
||||||
|
}, 3, 3, TimeUnit.SECONDS)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,797 @@
|
||||||
|
package com.github.catvod.api
|
||||||
|
|
||||||
|
import com.github.catvod.bean.Result
|
||||||
|
import com.github.catvod.bean.Vod
|
||||||
|
import com.github.catvod.bean.Vod.VodPlayBuilder
|
||||||
|
import com.github.catvod.net.OkHttp
|
||||||
|
import com.github.catvod.utils.Json
|
||||||
|
import com.github.catvod.utils.ProxyServer.buildProxyUrl
|
||||||
|
import com.github.catvod.utils.Util
|
||||||
|
import com.github.catvod.utils.Util.MEDIA
|
||||||
|
import com.google.gson.JsonObject
|
||||||
|
import java.net.URLEncoder
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
object BaiduDrive {
|
||||||
|
private val cache = mutableMapOf<String, String>();
|
||||||
|
|
||||||
|
private val headers = mapOf(
|
||||||
|
"User-Agent" to "Mozilla/5.0 (Linux; Android 12; SM-X800) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.40 Safari/537.36",
|
||||||
|
"Accept" to "application/json, text/plain, */*",
|
||||||
|
"Content-Type" to "application/x-www-form-urlencoded",
|
||||||
|
"Origin" to "https://pan.baidu.com",
|
||||||
|
"Referer" to "https://pan.baidu.com/"
|
||||||
|
)
|
||||||
|
|
||||||
|
//是否删除过文件标志
|
||||||
|
private var deleteTag = 0;
|
||||||
|
private val saveDirName = "TVBOX_BD"
|
||||||
|
|
||||||
|
private var cookies = BaiDuYunHandler.get().token
|
||||||
|
|
||||||
|
|
||||||
|
private val apiHost = "https://pan.baidu.com"
|
||||||
|
private val displayName = listOf("BD原画")
|
||||||
|
|
||||||
|
|
||||||
|
fun setCookie(extend: String) {
|
||||||
|
if (extend.isEmpty()) return
|
||||||
|
cookies = extend
|
||||||
|
}
|
||||||
|
|
||||||
|
fun processShareLinks(urls: List<String>): Pair<List<String>, List<String>> {
|
||||||
|
|
||||||
|
//首先确保cookie不为空
|
||||||
|
if (cookies.isEmpty()) {
|
||||||
|
BaiDuYunHandler.get().startScan()
|
||||||
|
cookies = BaiDuYunHandler.get().token
|
||||||
|
}
|
||||||
|
if (urls.isEmpty()) return emptyList<String>() to emptyList()
|
||||||
|
|
||||||
|
|
||||||
|
val results = urls.map { url ->
|
||||||
|
processSingleLink(url)
|
||||||
|
}
|
||||||
|
|
||||||
|
val names = mutableListOf<String>()
|
||||||
|
val allVideos = mutableListOf<String>()
|
||||||
|
|
||||||
|
results.forEach { result ->
|
||||||
|
if (result != null) {
|
||||||
|
val (avideos, videos) = result
|
||||||
|
names.addAll(displayName)
|
||||||
|
allVideos.add(avideos.joinToString("#"))
|
||||||
|
//allVideos.add(videos.joinToString("#"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return names to allVideos
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun processSingleLink(url: String): Pair<List<String>, List<String>>? {
|
||||||
|
return try {
|
||||||
|
val urlInfo = parseShareUrl(url)
|
||||||
|
if (urlInfo.containsKey("error")) return null
|
||||||
|
|
||||||
|
val tokenInfo = getShareToken(urlInfo)
|
||||||
|
if (tokenInfo?.containsKey("error") == true) return null
|
||||||
|
|
||||||
|
getAllVideos(tokenInfo!!)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseShareUrl(url: String): Map<String, String> {
|
||||||
|
var lurl = url
|
||||||
|
if ("提取码" in url) lurl = url.replace("提取码:", "?pwd=")
|
||||||
|
|
||||||
|
if ("/share/" !in url) {
|
||||||
|
val response = OkHttp.getLocation(url, headers)
|
||||||
|
lurl = response ?: ""
|
||||||
|
}
|
||||||
|
|
||||||
|
val queryParams = parseQueryParams(lurl)
|
||||||
|
val finalUrl = if ("/share/" in url) url.replace("share/init?surl=", "s/1") else url
|
||||||
|
|
||||||
|
return mapOf<String, String>(
|
||||||
|
"url" to finalUrl,
|
||||||
|
"surl" to (queryParams["surl"]?.firstOrNull() ?: ""),
|
||||||
|
"pwd" to (queryParams["pwd"]?.firstOrNull() ?: "")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getShareToken(urlInfo: Map<String, String>): Map<String, String>? {
|
||||||
|
val params = mapOf(
|
||||||
|
"t" to System.currentTimeMillis().toString(), "surl" to (urlInfo["surl"] ?: "")
|
||||||
|
)
|
||||||
|
|
||||||
|
val data = mapOf("pwd" to (urlInfo["pwd"] ?: ""))
|
||||||
|
|
||||||
|
val response = OkHttp.post(
|
||||||
|
"$apiHost/share/verify?t=${params["t"]}&surl=${params["surl"]}", data, headers
|
||||||
|
)
|
||||||
|
|
||||||
|
// if ("error" in response) return response
|
||||||
|
val json = Json.safeObject(response.body)
|
||||||
|
|
||||||
|
|
||||||
|
val randsk = json.asJsonObject.get("randsk").asString ?: return mapOf("error" to "获取randsk失败")
|
||||||
|
|
||||||
|
return mapOf(
|
||||||
|
"yurl" to (urlInfo["url"]?.split("s/")?.last()?.split("?")?.first() ?: ""),
|
||||||
|
"randsk" to randsk,
|
||||||
|
"surl" to (urlInfo["surl"] ?: "")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getAllVideos(tokenInfo: Map<String, String>): Pair<List<String>, List<String>> {
|
||||||
|
val videos = mutableListOf<String>()
|
||||||
|
val avideos = mutableListOf<String>()
|
||||||
|
val seenFolders = mutableSetOf<String>()
|
||||||
|
val pendingFolders = mutableListOf<Map<String, Any>>()
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 处理根目录
|
||||||
|
var currentPage = 1
|
||||||
|
var uk = ""
|
||||||
|
var shareid = ""
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
val rootFolder = mutableMapOf(
|
||||||
|
"surl" to tokenInfo["surl"]!!,
|
||||||
|
"randsk" to tokenInfo["randsk"]!!,
|
||||||
|
"page" to currentPage,
|
||||||
|
"is_root" to true
|
||||||
|
)
|
||||||
|
|
||||||
|
val rootResult = getFolderContents(rootFolder)
|
||||||
|
// if ("error" in rootResult) break
|
||||||
|
val data = rootResult?.asJsonObject
|
||||||
|
|
||||||
|
val items = data?.get("list")?.asJsonArray ?: break
|
||||||
|
|
||||||
|
if (items.isEmpty) break
|
||||||
|
|
||||||
|
// 第一页获取uk和shareid
|
||||||
|
if (currentPage == 1) {
|
||||||
|
uk = data["uk"]?.toString() ?: ""
|
||||||
|
shareid = data["share_id"]?.toString() ?: ""
|
||||||
|
if (uk.isEmpty() || shareid.isEmpty()) return emptyList<String>() to emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理items
|
||||||
|
items.forEach { item ->
|
||||||
|
if (item.asJsonObject["isdir"].asInt == 1) {
|
||||||
|
val folderPath =
|
||||||
|
"/sharelink$uk-${item.asJsonObject["fs_id"].asString}/${item.asJsonObject["server_filename"].asString}"
|
||||||
|
if (folderPath !in seenFolders) {
|
||||||
|
seenFolders.add(folderPath)
|
||||||
|
pendingFolders.add(
|
||||||
|
mapOf(
|
||||||
|
"surl" to tokenInfo["surl"]!!,
|
||||||
|
"randsk" to tokenInfo["randsk"]!!,
|
||||||
|
"uk" to uk,
|
||||||
|
"shareid" to shareid,
|
||||||
|
"dir" to folderPath,
|
||||||
|
"page" to 1
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else if (isVideoFile(item.asJsonObject["server_filename"].asString ?: "")) {
|
||||||
|
addVideo(item.asJsonObject, uk, shareid, tokenInfo, avideos, videos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (items.size() < 9999) break
|
||||||
|
currentPage++
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理子文件夹
|
||||||
|
while (pendingFolders.isNotEmpty()) {
|
||||||
|
val currentBatch = pendingFolders.toList()
|
||||||
|
pendingFolders.clear()
|
||||||
|
|
||||||
|
val results = currentBatch.map { folderInfo ->
|
||||||
|
getFolderContents(folderInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
results.forEachIndexed { i, result ->
|
||||||
|
val folderInfo = currentBatch[i]
|
||||||
|
//if ("error" in result) return@forEachIndexed
|
||||||
|
|
||||||
|
val items = result?.asJsonObject?.get("list")?.asJsonArray ?: return@forEachIndexed
|
||||||
|
|
||||||
|
items.forEach { item ->
|
||||||
|
if (item.asJsonObject["isdir"].asInt == 1) {
|
||||||
|
val folderPath = item.asJsonObject["path"].asString ?: ""
|
||||||
|
if (folderPath !in seenFolders) {
|
||||||
|
seenFolders.add(folderPath)
|
||||||
|
pendingFolders.add(
|
||||||
|
mapOf(
|
||||||
|
"surl" to tokenInfo["surl"]!!,
|
||||||
|
"randsk" to tokenInfo["randsk"]!!,
|
||||||
|
"uk" to folderInfo["uk"]!!,
|
||||||
|
"shareid" to folderInfo["shareid"]!!,
|
||||||
|
"dir" to folderPath,
|
||||||
|
"page" to 1
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else if (isVideoFile(item.asJsonObject["server_filename"].asString ?: "")) {
|
||||||
|
addVideo(
|
||||||
|
item.asJsonObject,
|
||||||
|
folderInfo["uk"]?.toString() ?: "",
|
||||||
|
folderInfo["shareid"]?.toString() ?: "",
|
||||||
|
tokenInfo,
|
||||||
|
avideos,
|
||||||
|
videos
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (items.size() >= 9999) {
|
||||||
|
pendingFolders.add(folderInfo.toMutableMap().apply {
|
||||||
|
this["page"] = (this["page"] as Int) + 1
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return avideos to videos
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
return emptyList<String>() to emptyList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isVideoFile(string: String): Boolean {
|
||||||
|
|
||||||
|
return string.substringAfterLast(".").lowercase(Locale.ROOT) in MEDIA
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun addVideo(
|
||||||
|
item: JsonObject,
|
||||||
|
uk: String,
|
||||||
|
shareid: String,
|
||||||
|
tokenInfo: Map<String, String>,
|
||||||
|
avideos: MutableList<String>,
|
||||||
|
videos: MutableList<String>
|
||||||
|
) {
|
||||||
|
val sizeStr = formatSize(item["size"].asLong)
|
||||||
|
val name = item["server_filename"] ?: ""
|
||||||
|
|
||||||
|
val originalData = mapOf(
|
||||||
|
"uk" to uk,
|
||||||
|
"shareid" to shareid,
|
||||||
|
"fid" to item["fs_id"],
|
||||||
|
"randsk" to tokenInfo["randsk"],
|
||||||
|
"pname" to name,
|
||||||
|
"qtype" to "original"
|
||||||
|
)
|
||||||
|
|
||||||
|
val previewData = mapOf(
|
||||||
|
"uk" to uk,
|
||||||
|
"fid" to item["fs_id"],
|
||||||
|
"shareid" to shareid,
|
||||||
|
"surl" to tokenInfo["yurl"],
|
||||||
|
"pname" to name,
|
||||||
|
"qtype" to "preview"
|
||||||
|
)
|
||||||
|
|
||||||
|
avideos.add(
|
||||||
|
"[$sizeStr]$name$${
|
||||||
|
Util.base64Encode(Json.toJson(originalData).toByteArray())
|
||||||
|
}"
|
||||||
|
)
|
||||||
|
videos.add(
|
||||||
|
"[$sizeStr]$name$${
|
||||||
|
Util.base64Encode(Json.toJson(previewData).toByteArray())
|
||||||
|
}"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getFolderContents(folderInfo: Map<String, Any>): JsonObject? {
|
||||||
|
val params = if (folderInfo.containsKey("dir")) {
|
||||||
|
mapOf(
|
||||||
|
"uk" to folderInfo["uk"]!!.toString(),
|
||||||
|
"shareid" to folderInfo["shareid"]!!.toString(),
|
||||||
|
"page" to folderInfo["page"].toString(),
|
||||||
|
"num" to "9999",
|
||||||
|
"dir" to URLEncoder.encode(folderInfo["dir"]!!.toString()),
|
||||||
|
"desc" to "0",
|
||||||
|
"order" to "name",
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
mapOf(
|
||||||
|
"page" to folderInfo["page"].toString(),
|
||||||
|
"num" to "9999",
|
||||||
|
"shorturl" to folderInfo["surl"]!!.toString(),
|
||||||
|
"root" to "1",
|
||||||
|
"desc" to "0",
|
||||||
|
"order" to "name",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
val tempHeader = headers.toMutableMap()
|
||||||
|
tempHeader.put("Cookie", "BDCLND=${folderInfo["randsk"]}")
|
||||||
|
val result = OkHttp.string("$apiHost/share/list", params, tempHeader)
|
||||||
|
return Json.safeObject(result)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun parseQueryParams(url: String): Map<String, List<String>> {
|
||||||
|
val query = url.substringAfter(
|
||||||
|
"?"
|
||||||
|
).substringBefore('#')
|
||||||
|
return query.split('&').associate {
|
||||||
|
val (key, value) = it.split('=', limit = 2)
|
||||||
|
key to listOf(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun formatSize(bytes: Long): String {
|
||||||
|
if (bytes <= 0) return "0 B"
|
||||||
|
val units = arrayOf("B", "KB", "MB", "GB", "TB")
|
||||||
|
val digitGroups = (Math.log10(bytes.toDouble()) / Math.log10(1024.0)).toInt()
|
||||||
|
return "%.1f %s".format(bytes / Math.pow(1024.0, digitGroups.toDouble()), units[digitGroups])
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun getBdUid(): String? {
|
||||||
|
if (cache["uid"] != null) {
|
||||||
|
return cache["uid"]
|
||||||
|
}
|
||||||
|
val tempHeader = headers.toMutableMap()
|
||||||
|
tempHeader.put("Cookie", cookies)
|
||||||
|
try {
|
||||||
|
val response = OkHttp.string(
|
||||||
|
|
||||||
|
"https://mbd.baidu.com/userx/v1/info/get?appname=baiduboxapp&fields=%20%20%20%20%20%20%20%20%5B%22bg_image%22,%22member%22,%22uid%22,%22avatar%22,%20%22avatar_member%22%5D&client&clientfrom&lang=zh-cn&tpl&ttt",
|
||||||
|
emptyMap<String, String>(),
|
||||||
|
tempHeader
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
val responseJson = Json.safeObject(response)
|
||||||
|
val user = responseJson["data"].asJsonObject
|
||||||
|
|
||||||
|
val fields = user?.get("fields")?.asJsonObject
|
||||||
|
|
||||||
|
val uidValue = fields?.get("uid")?.asString
|
||||||
|
|
||||||
|
if (uidValue != null) {
|
||||||
|
cache["uid"] = uidValue
|
||||||
|
return uidValue
|
||||||
|
} else {
|
||||||
|
throw Exception("Failed to retrieve UID from Baidu Drive.")
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
println("获取百度网盘用户ID失败: ${e.message}")
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun _getSign(videoData: JsonObject): Pair<String, String> {
|
||||||
|
val tempHeader = headers.toMutableMap()
|
||||||
|
tempHeader.put("Cookie", cookies)
|
||||||
|
val response: String? = OkHttp.string(
|
||||||
|
"${apiHost}/share/tplconfig", mapOf<String, String>(
|
||||||
|
"surl" to videoData["surl"].asString, "fields" to "cfrom_id,Espace_info,card_info,sign,timestamp"
|
||||||
|
),
|
||||||
|
|
||||||
|
tempHeader
|
||||||
|
)
|
||||||
|
return try {
|
||||||
|
val data = Json.safeObject(response)["data"].asJsonObject
|
||||||
|
|
||||||
|
data["sign"].asString to data["timestamp"].asString
|
||||||
|
} catch (_: Exception) {
|
||||||
|
"" to ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun _getDownloadUrl(videoData: JsonObject): String {
|
||||||
|
return try {
|
||||||
|
var cookie = ""
|
||||||
|
val BDCLND = "BDCLND=" + videoData["randsk"].asString
|
||||||
|
// 更新Cookie中的BDCLND值
|
||||||
|
if (!this.cookies.contains("BDCLND")) {
|
||||||
|
cookie = this.cookies + ";" + BDCLND
|
||||||
|
|
||||||
|
} else {
|
||||||
|
cookie = this.cookies.split(";").joinToString(";") {
|
||||||
|
if (it.contains("BDCLND")) {
|
||||||
|
BDCLND
|
||||||
|
} else {
|
||||||
|
it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
val transferHeaders = mapOf(
|
||||||
|
"User-Agent" to "Android",
|
||||||
|
"Connection" to "Keep-Alive",
|
||||||
|
"Content-Type" to "application/x-www-form-urlencoded",
|
||||||
|
"Accept-Language" to "zh-CN,zh;q=0.8",
|
||||||
|
"charset" to "UTF-8",
|
||||||
|
"Referer" to "https://pan.baidu.com",
|
||||||
|
"Cookie" to cookie
|
||||||
|
)
|
||||||
|
// 先清空文件夹在创建文件夹
|
||||||
|
|
||||||
|
if (deleteTag == 0) {
|
||||||
|
_deleteTransferFile("/$saveDirName")
|
||||||
|
//创建路径
|
||||||
|
createSaveDir()
|
||||||
|
deleteTag = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
val data =
|
||||||
|
"from=${videoData["uk"].asString}&shareid=${videoData["shareid"].asString}&ondup=newcopy&path=/${saveDirName}&fsidlist=[${videoData["fid"].asString}]"
|
||||||
|
|
||||||
|
var to = ""
|
||||||
|
for (i in 1..30) {
|
||||||
|
|
||||||
|
|
||||||
|
val response =
|
||||||
|
OkHttp.post("${apiHost}/share/transfer?${data}", emptyMap<String, String>(), transferHeaders)
|
||||||
|
|
||||||
|
val result = Json.safeObject(response.body)
|
||||||
|
|
||||||
|
try {
|
||||||
|
to = (result["extra"].asJsonObject)["list"].asJsonArray[0].asJsonObject["to"].asString
|
||||||
|
// videoData["to"] = to
|
||||||
|
if (to.isNotEmpty()) {
|
||||||
|
println("成功转存文件到: $to")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
println("解析转存响应出错: ${e.message}")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (to.isEmpty()) {
|
||||||
|
println("转存文件失败,无法获取下载链接")
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
val mediaInfoHeaders = mapOf(
|
||||||
|
|
||||||
|
"User-Agent" to "netdisk;1.4.2;22021211RC;android-android;12;JSbridge4.4.0;jointBridge;1.1.0;",
|
||||||
|
"Connection" to "Keep-Alive",
|
||||||
|
"Accept-Language" to "zh-CN,zh;q=0.8",
|
||||||
|
"charset" to "UTF-8",
|
||||||
|
"Cookie" to cookie
|
||||||
|
).toMutableMap()
|
||||||
|
|
||||||
|
|
||||||
|
val mediaInfoParams = mapOf(
|
||||||
|
"type" to "M3U8_FLV_264_480", "path" to "/$to", "clienttype" to "80", "origin" to "dlna"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
val mediaInfoResponse: String? = OkHttp.string(
|
||||||
|
"${apiHost}/api/mediainfo", mediaInfoParams, mediaInfoHeaders
|
||||||
|
)
|
||||||
|
val responseJson = Json.safeObject(mediaInfoResponse)
|
||||||
|
val info = responseJson["info"].asJsonObject
|
||||||
|
val downloadUrl = info["dlink"].asString
|
||||||
|
println("获取到下载链接: $downloadUrl")
|
||||||
|
downloadUrl
|
||||||
|
} catch (e: Exception) {
|
||||||
|
println("获取下载链接过程中出错: ${e.message}")
|
||||||
|
e.printStackTrace()
|
||||||
|
""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getVideoUrl(videoData: JsonObject, flag: String): Map<String, Any> {
|
||||||
|
return try {
|
||||||
|
val bdUid = getBdUid()
|
||||||
|
println("获取百度网盘用户ID: $bdUid")
|
||||||
|
|
||||||
|
if (flag.contains("原画")) {
|
||||||
|
|
||||||
|
var headersApp = mapOf("User-Agent" to "netdisk;P2SP;2.2.91.136;android-android;")
|
||||||
|
|
||||||
|
var downloadUrl = _getAppDownloadUrl(videoData)
|
||||||
|
if (downloadUrl.isEmpty()) {
|
||||||
|
|
||||||
|
headersApp = mapOf(
|
||||||
|
|
||||||
|
"User-Agent" to "netdisk;1.4.2;22021211RC;android-android;12;JSbridge4.4.0;jointBridge;1.1.0;"
|
||||||
|
)
|
||||||
|
|
||||||
|
downloadUrl = _getDownloadUrl(videoData)
|
||||||
|
}
|
||||||
|
if (downloadUrl.isNotEmpty()) {
|
||||||
|
|
||||||
|
val result = mapOf(
|
||||||
|
"parse" to "0", "url" to downloadUrl, "header" to headersApp
|
||||||
|
)
|
||||||
|
|
||||||
|
result
|
||||||
|
} else {
|
||||||
|
_handleError
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val (sign, time) = _getSign(videoData)
|
||||||
|
if (sign.isEmpty() || time.isEmpty()) {
|
||||||
|
return _handleError
|
||||||
|
}
|
||||||
|
val plist = _getPlayList(videoData, sign, time)
|
||||||
|
val tempHeader = headers.toMutableMap()
|
||||||
|
tempHeader.put("Cookie", cookies)
|
||||||
|
mapOf(
|
||||||
|
"parse" to "0", "url" to plist[0], "header" to tempHeader
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
println("获取播放链接失败: ${e.message}")
|
||||||
|
_handleError
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun _getAppDownloadUrl(videoData: JsonObject): String {
|
||||||
|
return try {
|
||||||
|
val headers = mapOf<String, String>(
|
||||||
|
|
||||||
|
"User-Agent" to "netdisk;P2SP;2.2.91.136;android-android;",
|
||||||
|
"Connection" to "Keep-Alive",
|
||||||
|
"Accept-Language" to "zh-CN,zh;q=0.8",
|
||||||
|
"charset" to "UTF-8",
|
||||||
|
"cookie" to cookies
|
||||||
|
)
|
||||||
|
val uid = this.getBdUid()
|
||||||
|
val t = System.currentTimeMillis()
|
||||||
|
val params = mapOf<String, String>(
|
||||||
|
"shareid" to videoData["shareid"].asString,
|
||||||
|
"uk" to videoData["uk"].asString,
|
||||||
|
"fid" to videoData["fid"].asString,
|
||||||
|
"sekey" to unquote(videoData["randsk"].asString),
|
||||||
|
"origin" to "dlna",
|
||||||
|
"devuid" to "73CED981D0F186D12BC18CAE1684FFD5|VSRCQTF6W",
|
||||||
|
"clienttype" to "1",
|
||||||
|
"channel" to "android_12_zhao_bd-netdisk_1024266h",
|
||||||
|
"version" to "11.30.2",
|
||||||
|
"time" to t.toString()
|
||||||
|
).toMutableMap()
|
||||||
|
|
||||||
|
val randstr = this.sha1(
|
||||||
|
this.sha1(
|
||||||
|
Util.findByRegex(
|
||||||
|
"BDUSS=(.+?);", cookies, 1
|
||||||
|
)
|
||||||
|
) + uid + "ebrcUYiuxaZv2XGu7KIYKxUrqfnOfpDF$t${params["devuid"]}11.30.2ae5821440fab5e1a61a025f014bd8972"
|
||||||
|
)
|
||||||
|
|
||||||
|
params.put("rand", randstr)
|
||||||
|
|
||||||
|
val response = OkHttp.string(
|
||||||
|
"${apiHost}/share/list", params, headers
|
||||||
|
)
|
||||||
|
val json = Json.safeObject(response)
|
||||||
|
val dlink = json["list"].asJsonArray[0].asJsonObject["dlink"].asString
|
||||||
|
|
||||||
|
/* val url = response["data"] as Map<String, Any>
|
||||||
|
val list = url["list"] as List<Map<String, Any>>
|
||||||
|
val dlink = list[0]["dlink"] as String
|
||||||
|
|
||||||
|
val pDataResponse = client.get(dlink) {
|
||||||
|
headers { this@_getAppDownloadUrl.headers.forEach { name, value -> append(name, value) } }
|
||||||
|
cookies?.let { setCookie(it) }
|
||||||
|
followRedirects = false
|
||||||
|
timeout.socketTimeoutMillis = 10000
|
||||||
|
}
|
||||||
|
|
||||||
|
val pUrl = pDataResponse.headers[HttpHeaders.Location]?.toString()
|
||||||
|
pUrl ?: dlink*/
|
||||||
|
dlink
|
||||||
|
} catch (e: Exception) {
|
||||||
|
println("获取下载链接失败: ${e.message}")
|
||||||
|
""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun _getPlayList(videoData: JsonObject, sign: String, time: String): List<String> {
|
||||||
|
val hz = getPlayFormatList()
|
||||||
|
val plist = mutableListOf<String>()
|
||||||
|
|
||||||
|
for (quality in hz) {
|
||||||
|
val url =
|
||||||
|
("${apiHost}/share/streaming?" + "uk=${videoData["uk"].asString}&" + "fid=${videoData["fid"].asString}&" + "sign=$sign&" + "timestamp=$time&" + "shareid=${videoData["shareid"].asString}&" + "type=M3U8_AUTO_${
|
||||||
|
quality.replace(
|
||||||
|
"P", ""
|
||||||
|
)
|
||||||
|
}")
|
||||||
|
plist.add(url)
|
||||||
|
}
|
||||||
|
|
||||||
|
return plist
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建保存目录
|
||||||
|
*/
|
||||||
|
fun createSaveDir(): Long? {
|
||||||
|
var saveDirId: Long? = null
|
||||||
|
// 创建保存目录
|
||||||
|
if (cookies.isEmpty()) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
val tempHeader = headers.toMutableMap()
|
||||||
|
tempHeader.put("Cookie", cookies)
|
||||||
|
return try {
|
||||||
|
val listResp = OkHttp.string(
|
||||||
|
"${apiHost}/api/list", mapOf(
|
||||||
|
"dir" to "/",
|
||||||
|
"order" to "name",
|
||||||
|
"desc" to "0",
|
||||||
|
"showempty" to "0",
|
||||||
|
"web" to "1",
|
||||||
|
"app_id" to "250528"
|
||||||
|
), tempHeader
|
||||||
|
)
|
||||||
|
val json = Json.safeObject(listResp)
|
||||||
|
|
||||||
|
if (json["errno"].asInt != 0) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
val drpyDir = json["list"].asJsonArray.find { item ->
|
||||||
|
item.asJsonObject.get("isdir").asInt == 1 && item.asJsonObject.get("server_filename").asString == saveDirName
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (drpyDir != null) {
|
||||||
|
saveDirId = drpyDir.asJsonObject.get("fs_id").asLong
|
||||||
|
return saveDirId
|
||||||
|
}
|
||||||
|
|
||||||
|
val createResp = OkHttp.post(
|
||||||
|
"${apiHost}/api/create?a=commit&bdstoken=${getBdstoken()}&clienttype=0&app_id=250528&web=1&dp-logid=73131200762376420075",
|
||||||
|
mapOf(
|
||||||
|
"path" to "//$saveDirName",
|
||||||
|
"isdir" to "1",
|
||||||
|
"block_list" to "[]",
|
||||||
|
|
||||||
|
),
|
||||||
|
tempHeader
|
||||||
|
)
|
||||||
|
val createJson = Json.safeObject(createResp.body)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
saveDirId = createJson["fs_id"].asLong
|
||||||
|
saveDirId
|
||||||
|
|
||||||
|
} catch (e: Exception) {
|
||||||
|
println("创建保存目录失败: ${e.message}")
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getBdstoken(): String {
|
||||||
|
if (cache["bdstoken"] != null) {
|
||||||
|
return cache["bdstoken"]!!
|
||||||
|
}
|
||||||
|
val tempHeader = headers.toMutableMap()
|
||||||
|
tempHeader.put("Cookie", cookies)
|
||||||
|
val userInfo = OkHttp.string(
|
||||||
|
"${apiHost}/api/gettemplatevariable?clienttype=0&app_id=250528&web=1&fields=[\"bdstoken\",\"token\",\"uk\",\"isdocuser\",\"servertime\"]",
|
||||||
|
mapOf(),
|
||||||
|
tempHeader
|
||||||
|
|
||||||
|
)
|
||||||
|
val json = Json.safeObject(userInfo)
|
||||||
|
|
||||||
|
val bdstoken: String? = json["result"]?.asJsonObject?.get("bdstoken")?.asString
|
||||||
|
cache["bdstoken"] = bdstoken ?: ""
|
||||||
|
return bdstoken ?: ""
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun _deleteTransferFile(filePath: String) {
|
||||||
|
try {
|
||||||
|
val url =
|
||||||
|
"$apiHost/api/filemanager?async=2&onnest=fail&opera=delete&bdstoken=${getBdstoken()}&newVerify=1&clienttype=0&app_id=250528&web=1&dp-logid=39292100213290200076"
|
||||||
|
val params = mapOf(
|
||||||
|
"filelist" to "[\"$filePath\"]"
|
||||||
|
)
|
||||||
|
|
||||||
|
val headers = mutableMapOf(
|
||||||
|
"User-Agent" to "Android",
|
||||||
|
"Connection" to "Keep-Alive",
|
||||||
|
"Accept-Encoding" to "br,gzip",
|
||||||
|
"Content-Type" to "application/x-www-form-urlencoded; charset=utf-8",
|
||||||
|
"Accept-Language" to "zh-CN,zh;q=0.8",
|
||||||
|
"charset" to "UTF-8",
|
||||||
|
"Cookie" to cookies,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
val response = OkHttp.post(url, params, headers)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
println("删除文件响应: ${response.body}")
|
||||||
|
println("响应状态码: ${response.code}")
|
||||||
|
} catch (e: Exception) {
|
||||||
|
println("删除文件出错: ${e.message}")
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun unquote(encoded: String): String {
|
||||||
|
return encoded.replace("%([0-9A-Fa-f]{2})".toRegex()) { match ->
|
||||||
|
Integer.parseInt(match.groupValues[1], 16).toChar().toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun sha1(input: String): String {
|
||||||
|
|
||||||
|
return Util.sha1Hex(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val _handleError = mapOf(
|
||||||
|
"parse" to "1", "msg" to "Error retrieving video URL"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
fun getVod(shareUrl: String): Vod {
|
||||||
|
val (froms, urls) = processShareLinks(listOf(shareUrl))
|
||||||
|
val builder = VodPlayBuilder()
|
||||||
|
for (i in froms.indices) {
|
||||||
|
val playUrls = mutableListOf<VodPlayBuilder.PlayUrl>();
|
||||||
|
for (url in urls[i].split("#")) {
|
||||||
|
val arr = url.split("$")
|
||||||
|
val play = VodPlayBuilder.PlayUrl()
|
||||||
|
play.name = arr[0]
|
||||||
|
play.url = arr[1]
|
||||||
|
|
||||||
|
playUrls.add(play)
|
||||||
|
|
||||||
|
}
|
||||||
|
builder.append(froms[i], playUrls)
|
||||||
|
}
|
||||||
|
val buildResult = builder.build();
|
||||||
|
|
||||||
|
val vod = Vod()
|
||||||
|
vod.setVodId(shareUrl)
|
||||||
|
vod.setVodPic("")
|
||||||
|
vod.setVodYear("")
|
||||||
|
vod.setVodName("")
|
||||||
|
vod.setVodContent("")
|
||||||
|
vod.setVodPlayFrom(buildResult.vodPlayFrom)
|
||||||
|
vod.setVodPlayUrl(buildResult.vodPlayUrl)
|
||||||
|
return vod
|
||||||
|
}
|
||||||
|
|
||||||
|
fun playerContent(json: JsonObject, flag: String): String {
|
||||||
|
val play = getVideoUrl(json, flag);
|
||||||
|
val header = play["header"] as Map<String, String>
|
||||||
|
return Result.get().url(buildProxyUrl(play["url"] as String, header)).octet().header(header).string();
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getPlayFormatList(): Array<String> {
|
||||||
|
return listOf("1080P").toTypedArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun proxyVideo(params: MutableMap<String, String>): Array<Any> {
|
||||||
|
return emptyList<Any>().toTypedArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,398 @@
|
||||||
|
package com.github.catvod.api
|
||||||
|
|
||||||
|
import com.github.catvod.bean.Result
|
||||||
|
import com.github.catvod.bean.Vod
|
||||||
|
import com.github.catvod.bean.Vod.VodPlayBuilder
|
||||||
|
import com.github.catvod.bean.pan123.ShareInfo
|
||||||
|
import com.github.catvod.crawler.SpiderDebug
|
||||||
|
import com.github.catvod.net.OkHttp
|
||||||
|
import com.github.catvod.utils.Json
|
||||||
|
import com.github.catvod.utils.ProxyServer.buildProxyUrl
|
||||||
|
import com.github.catvod.utils.Util
|
||||||
|
import com.google.gson.JsonObject
|
||||||
|
import okhttp3.HttpUrl
|
||||||
|
import java.util.regex.Pattern
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 123网盘API操作类
|
||||||
|
* 提供123网盘的文件分享、下载、播放等功能
|
||||||
|
*/
|
||||||
|
object Pan123Api {
|
||||||
|
public const val regex =
|
||||||
|
"https://(123592\\.com|123912\\.com|123865\\.com|123684\\.com|www\\.123684\\.com|www\\.123865\\.com|www\\.123912\\.com|www\\.123pan\\.com|www\\.123pan\\.cn|www\\.123592\\.com)/s/([^/]+)"
|
||||||
|
|
||||||
|
private const val api = "https://www.123684.com/b/api/share/"
|
||||||
|
private const val loginUrl = "https://login.123pan.com/api/user/sign_in"
|
||||||
|
|
||||||
|
private var authToken = ""
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化方法,检查登录状态
|
||||||
|
*/
|
||||||
|
fun init() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取认证token
|
||||||
|
*/
|
||||||
|
private fun getAuth(): String {
|
||||||
|
if (authToken.isNotBlank()) {
|
||||||
|
return authToken
|
||||||
|
}
|
||||||
|
return Pan123Handler.getAuth()
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录方法
|
||||||
|
*/
|
||||||
|
fun login(passport: String, password: String): JsonObject? {
|
||||||
|
|
||||||
|
val data = mapOf(
|
||||||
|
"passport" to passport, "password" to password, "remember" to true
|
||||||
|
)
|
||||||
|
|
||||||
|
val headers = mapOf(
|
||||||
|
"User-Agent" to "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36",
|
||||||
|
"Content-Type" to "application/json",
|
||||||
|
"App-Version" to "43",
|
||||||
|
"Referer" to "https://login.123pan.com/centerlogin?redirect_url=https%3A%2F%2Fwww.123684.com&source_page=website",
|
||||||
|
"Accept" to "application/json, text/plain, */*",
|
||||||
|
"Origin" to "https://login.123pan.com",
|
||||||
|
"Connection" to "keep-alive",
|
||||||
|
"Accept-Language" to "zh-CN,zh;q=0.9"
|
||||||
|
)
|
||||||
|
|
||||||
|
try {
|
||||||
|
val response = OkHttp.post(loginUrl, Json.toJson(data), headers)
|
||||||
|
SpiderDebug.log("登录请求信息:")
|
||||||
|
SpiderDebug.log("URL: $loginUrl")
|
||||||
|
SpiderDebug.log("账号: ${passport}")
|
||||||
|
SpiderDebug.log("响应内容: ${response.body}")
|
||||||
|
|
||||||
|
if (response.code == 200) {
|
||||||
|
val authData = Json.safeObject(response.body)
|
||||||
|
SpiderDebug.log("解析后的响应数据: $authData")
|
||||||
|
if (authData.has("data") && authData.getAsJsonObject("data").has("token")) {
|
||||||
|
val token = authData.getAsJsonObject("data").get("token").asString
|
||||||
|
// setAuth(token)
|
||||||
|
SpiderDebug.log("登录成功")
|
||||||
|
authToken=token
|
||||||
|
|
||||||
|
return authData.get("data").asJsonObject
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw Exception("登录失败: HTTP状态码=${response.code}, 响应=${response.body}")
|
||||||
|
} catch (e: Exception) {
|
||||||
|
SpiderDebug.log("登录过程中发生错误: ${e.message}")
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析分享链接,提取分享密钥和提取码
|
||||||
|
*/
|
||||||
|
fun getShareData(url: String): Map<String, String> {
|
||||||
|
SpiderDebug.log("123链接:$url")
|
||||||
|
var sharePwd = ""
|
||||||
|
var lurl = java.net.URLDecoder.decode(url, "UTF-8")
|
||||||
|
|
||||||
|
// 处理提取码格式
|
||||||
|
if ("提取码" in lurl && "?" !in lurl) {
|
||||||
|
lurl = lurl.replace(Regex("提取码[::]"), "?")
|
||||||
|
}
|
||||||
|
if ("提取码" in lurl && "?" in lurl) {
|
||||||
|
lurl = lurl.replace(Regex("提取码[::]?"), "")
|
||||||
|
}
|
||||||
|
if (":" in lurl) {
|
||||||
|
lurl = lurl.replace(":", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
val matcher = Pattern.compile(regex).matcher(lurl)
|
||||||
|
|
||||||
|
// 提取分享密码
|
||||||
|
if ("?" in lurl) {
|
||||||
|
val queryPart = lurl.split("?")[1]
|
||||||
|
val pwdMatcher = Regex("[A-Za-z0-9]+").find(queryPart)
|
||||||
|
if (pwdMatcher != null) {
|
||||||
|
sharePwd = queryPart.split("=")[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matcher.find()) {
|
||||||
|
val match = matcher.group(2) ?: ""
|
||||||
|
val key = when {
|
||||||
|
"?" in match -> match.split("?")[0]
|
||||||
|
".html" in match -> match.replace(".html", "")
|
||||||
|
"www" in match -> matcher.group(1) ?: match
|
||||||
|
else -> match
|
||||||
|
}
|
||||||
|
return mapOf("key" to key, "sharePwd" to sharePwd)
|
||||||
|
}
|
||||||
|
|
||||||
|
return emptyMap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据分享链接获取文件列表
|
||||||
|
*/
|
||||||
|
fun getFilesByShareUrl(shareKey: String, sharePwd: String): List<ShareInfo> {
|
||||||
|
// 获取分享信息
|
||||||
|
val cate = getShareInfo(shareKey, sharePwd, 0, 0)
|
||||||
|
|
||||||
|
return cate
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取分享信息
|
||||||
|
*/
|
||||||
|
private fun getShareInfo(
|
||||||
|
shareKey: String, sharePwd: String, next: Int, parentFileId: Long
|
||||||
|
): List<ShareInfo> {
|
||||||
|
|
||||||
|
|
||||||
|
//文件夹
|
||||||
|
val folders = mutableListOf<ShareInfo>()
|
||||||
|
//视频
|
||||||
|
val videos = mutableListOf<ShareInfo>()
|
||||||
|
|
||||||
|
|
||||||
|
val url =
|
||||||
|
"${api}get?limit=100&next=$next&orderBy=file_name&orderDirection=asc&shareKey=${shareKey.trim()}&SharePwd=${sharePwd.ifEmpty { "" }}&ParentFileId=$parentFileId&Page=1"
|
||||||
|
|
||||||
|
try {
|
||||||
|
val response = OkHttp.string(
|
||||||
|
url, mapOf(
|
||||||
|
"User-Agent" to "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
val data = Json.safeObject(response)
|
||||||
|
if (data.has("code") && data.get("code").asInt == 5103) {
|
||||||
|
SpiderDebug.log("123获取文件列表出错:" + data.get("message").asString)
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
val info = data.getAsJsonObject("data")
|
||||||
|
//其下没有文件
|
||||||
|
if (info.get("Len").asLong <= 0) {
|
||||||
|
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
|
val nextValue = info.get("Next").asInt
|
||||||
|
val infoList = info.getAsJsonArray("InfoList")
|
||||||
|
|
||||||
|
// 处理文件夹
|
||||||
|
for (item in infoList) {
|
||||||
|
val itemObj = item.asJsonObject
|
||||||
|
//文件夹
|
||||||
|
if (itemObj.get("Category").asInt == 0) {
|
||||||
|
folders.add(
|
||||||
|
ShareInfo(
|
||||||
|
itemObj.get("FileName").asString,
|
||||||
|
shareKey,
|
||||||
|
sharePwd,
|
||||||
|
nextValue,
|
||||||
|
itemObj.get("FileId").asLong,
|
||||||
|
itemObj["S3KeyFlag"].asString,
|
||||||
|
itemObj["Size"].asLong,
|
||||||
|
itemObj["Etag"].asString,
|
||||||
|
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} else if (itemObj.get("Category").asInt == 2) {
|
||||||
|
videos.add(
|
||||||
|
ShareInfo(
|
||||||
|
itemObj.get("FileName").asString,
|
||||||
|
shareKey,
|
||||||
|
sharePwd,
|
||||||
|
nextValue,
|
||||||
|
itemObj.get("FileId").asLong,
|
||||||
|
itemObj["S3KeyFlag"].asString,
|
||||||
|
itemObj["Size"].asLong,
|
||||||
|
itemObj["Etag"].asString,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 递归获取子文件夹信息
|
||||||
|
|
||||||
|
for (item in folders) {
|
||||||
|
val result = getShareInfo(
|
||||||
|
item.shareKey, item.sharePwd, item.next, item.fileId
|
||||||
|
)
|
||||||
|
videos.addAll(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
return videos
|
||||||
|
} catch (e: Exception) {
|
||||||
|
SpiderDebug.log("获取分享信息时发生错误: ${e.message}")
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取文件下载链接
|
||||||
|
*/
|
||||||
|
fun getDownload(
|
||||||
|
shareKey: String, fileId: Long, s3KeyFlag: String, size: Long, etag: String
|
||||||
|
): String {
|
||||||
|
|
||||||
|
|
||||||
|
SpiderDebug.log("获取下载链接参数:")
|
||||||
|
SpiderDebug.log("ShareKey: $shareKey")
|
||||||
|
SpiderDebug.log("FileID: $fileId")
|
||||||
|
SpiderDebug.log("S3KeyFlag: $s3KeyFlag")
|
||||||
|
SpiderDebug.log("Size: $size")
|
||||||
|
SpiderDebug.log("Etag: $etag")
|
||||||
|
SpiderDebug.log("Auth Token: ${getAuth().take(30)}...")
|
||||||
|
|
||||||
|
val data = mapOf(
|
||||||
|
"ShareKey" to shareKey, "FileID" to fileId, "S3KeyFlag" to s3KeyFlag, "Size" to size, "Etag" to etag
|
||||||
|
)
|
||||||
|
|
||||||
|
val url = "${api}download/info"
|
||||||
|
SpiderDebug.log("请求URL: $url")
|
||||||
|
SpiderDebug.log("请求数据: ${Json.toJson(data)}")
|
||||||
|
|
||||||
|
val headers = mapOf(
|
||||||
|
"User-Agent" to "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36",
|
||||||
|
"Content-Type" to "application/json",
|
||||||
|
"Authorization" to "Bearer ${getAuth()}",
|
||||||
|
"platform" to "android"
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
try {
|
||||||
|
val response = OkHttp.post(url, Json.toJson(data), headers)
|
||||||
|
SpiderDebug.log("响应状态码: ${response.code}")
|
||||||
|
SpiderDebug.log("响应内容: ${response.body}")
|
||||||
|
|
||||||
|
if (response.code == 200) {
|
||||||
|
val responseData = Json.safeObject(response.body)
|
||||||
|
if (responseData.has("data") && responseData.getAsJsonObject("data").has("DownloadURL")) {
|
||||||
|
val encodeUrl = responseData.getAsJsonObject("data").get("DownloadURL").asString
|
||||||
|
return Util.base64Decode(HttpUrl.parse(encodeUrl)?.queryParameter("params"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw Exception("获取下载链接失败: HTTP状态码=${response.code}, 响应=${response.body}")
|
||||||
|
} catch (e: Exception) {
|
||||||
|
SpiderDebug.log("获取下载链接时发生错误: ${e.message}")
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取视频在线播放链接
|
||||||
|
*/
|
||||||
|
fun getLiveTranscoding(
|
||||||
|
shareKey: String, fileId: Long, s3KeyFlag: String, size: Long, etag: String
|
||||||
|
): List<Map<String, String>> {
|
||||||
|
|
||||||
|
|
||||||
|
val url = "https://www.123684.com/b/api/video/play/info?" + "etag=$etag&size=$size&from=1&shareKey=$shareKey"
|
||||||
|
|
||||||
|
val headers = mapOf(
|
||||||
|
"User-Agent" to "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36",
|
||||||
|
"Authorization" to "Bearer ${getAuth()}",
|
||||||
|
"Content-Type" to "application/json;charset=UTF-8",
|
||||||
|
"platform" to "android"
|
||||||
|
)
|
||||||
|
|
||||||
|
try {
|
||||||
|
val response = OkHttp.string(url, headers)
|
||||||
|
|
||||||
|
val downData = Json.safeObject(response)
|
||||||
|
if (downData.has("data") && downData.getAsJsonObject("data").has("video_play_info")) {
|
||||||
|
val videoInfo = mutableListOf<Map<String, String>>()
|
||||||
|
val videoPlayInfo = downData.getAsJsonObject("data").getAsJsonArray("video_play_info")
|
||||||
|
|
||||||
|
for (item in videoPlayInfo) {
|
||||||
|
val itemObj = item.asJsonObject
|
||||||
|
if (itemObj.has("url") && !itemObj.get("url").isJsonNull) {
|
||||||
|
val resolution = if (itemObj.has("resolution")) itemObj.get("resolution").asString else ""
|
||||||
|
val urlValue = itemObj.get("url").asString
|
||||||
|
|
||||||
|
videoInfo.add(
|
||||||
|
mapOf(
|
||||||
|
"name" to resolution, "url" to urlValue
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return videoInfo
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
SpiderDebug.log("获取视频播放链接时发生错误: ${e.message}")
|
||||||
|
}
|
||||||
|
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getPlayFormatList(): Array<String> {
|
||||||
|
return arrayOf("原画")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getVod(key: String, sharePwd: String): Vod {
|
||||||
|
val list = getFilesByShareUrl(key, sharePwd)
|
||||||
|
|
||||||
|
val builder = VodPlayBuilder()
|
||||||
|
for (i in getPlayFormatList().indices) {
|
||||||
|
val playUrls = mutableListOf<VodPlayBuilder.PlayUrl>();
|
||||||
|
for (item in list) {
|
||||||
|
|
||||||
|
val play = VodPlayBuilder.PlayUrl()
|
||||||
|
play.name = "[${Util.getSize(item.Size.toDouble())}]${item.filename}"
|
||||||
|
play.url =
|
||||||
|
listOf(item.shareKey, item.fileId, item.S3KeyFlag, item.Size, item.Etag).joinToString("\\+\\+")
|
||||||
|
|
||||||
|
playUrls.add(play)
|
||||||
|
|
||||||
|
}
|
||||||
|
builder.append("pan123" + getPlayFormatList()[i], playUrls)
|
||||||
|
}
|
||||||
|
val buildResult = builder.build();
|
||||||
|
|
||||||
|
val vod = Vod()
|
||||||
|
vod.setVodId(key + "++" + sharePwd)
|
||||||
|
vod.setVodPic("")
|
||||||
|
vod.setVodYear("")
|
||||||
|
vod.setVodName("")
|
||||||
|
vod.setVodContent("")
|
||||||
|
vod.setVodPlayFrom(buildResult.vodPlayFrom)
|
||||||
|
vod.setVodPlayUrl(buildResult.vodPlayUrl)
|
||||||
|
return vod
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun playerContent(id: String, flag: String): String {
|
||||||
|
val itemJson = id.split("\\+\\+")
|
||||||
|
|
||||||
|
SpiderDebug.log("播放参数:$itemJson")
|
||||||
|
val url = getDownload(
|
||||||
|
itemJson[0], itemJson[1].toLong(), itemJson[2], itemJson[3].toLong(), itemJson[4]
|
||||||
|
)
|
||||||
|
val header = mapOf(
|
||||||
|
"User-Agent" to "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36"
|
||||||
|
)
|
||||||
|
|
||||||
|
return Result.get().url(buildProxyUrl(url, header)).octet().header(header).string();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,142 @@
|
||||||
|
package com.github.catvod.api
|
||||||
|
|
||||||
|
|
||||||
|
import android.R
|
||||||
|
import android.app.AlertDialog
|
||||||
|
import android.content.DialogInterface
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.EditText
|
||||||
|
import android.widget.LinearLayout
|
||||||
|
import com.github.catvod.api.Pan123Api.login
|
||||||
|
import com.github.catvod.bean.pan123.Cache
|
||||||
|
import com.github.catvod.bean.pan123.User
|
||||||
|
import com.github.catvod.crawler.SpiderDebug
|
||||||
|
import com.github.catvod.spider.Init
|
||||||
|
import com.github.catvod.utils.Notify
|
||||||
|
import com.github.catvod.utils.Path
|
||||||
|
import com.github.catvod.utils.ResUtil
|
||||||
|
import org.apache.commons.lang3.StringUtils
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
object Pan123Handler {
|
||||||
|
|
||||||
|
|
||||||
|
private var cache: Cache? = null
|
||||||
|
private var dialog: AlertDialog? = null
|
||||||
|
|
||||||
|
private var auth = ""
|
||||||
|
private var userName = ""
|
||||||
|
private var passwd = ""
|
||||||
|
private var expire = 0L;
|
||||||
|
|
||||||
|
fun getCache(): File {
|
||||||
|
return Path.tv("pan123")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getAuth(): String {
|
||||||
|
return auth
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
init {
|
||||||
|
|
||||||
|
cache = Cache.objectFrom(Path.read(getCache()))
|
||||||
|
if (cache == null) {
|
||||||
|
SpiderDebug.log("cache 为null")
|
||||||
|
startFlow()
|
||||||
|
} else {
|
||||||
|
userName = cache!!.user.userName
|
||||||
|
passwd = cache!!.user.password
|
||||||
|
auth = cache!!.user.cookie
|
||||||
|
expire = cache!!.user.expire
|
||||||
|
if (StringUtils.isNotBlank(userName) && StringUtils.isNotBlank(passwd)) {
|
||||||
|
if (StringUtils.isBlank(auth) || expire == 0L || System.currentTimeMillis() > expire) {
|
||||||
|
SpiderDebug.log("auth 为空或者登录已过期")
|
||||||
|
this.loginWithPassword(userName, passwd)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
SpiderDebug.log("userName passwd 为空")
|
||||||
|
startFlow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun loginWithPassword(uname: String?, passwd: String?) {
|
||||||
|
SpiderDebug.log("loginWithPassword uname: $uname,passwd:$passwd")
|
||||||
|
|
||||||
|
try {
|
||||||
|
//保存的账号密码
|
||||||
|
val json = login(uname!!, passwd!!)
|
||||||
|
if (json != null) {
|
||||||
|
val user = User()
|
||||||
|
user.cookie = json.get("token").asString
|
||||||
|
user.password = passwd
|
||||||
|
user.userName = uname
|
||||||
|
user.expire = json.get("refresh_token_expire_time").asLong * 1000
|
||||||
|
this.auth = json.get("token").asString
|
||||||
|
cache?.setUserInfo(user)
|
||||||
|
Notify.show("123登录成功")
|
||||||
|
} else {
|
||||||
|
Notify.show("123登录失败")
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (e: Exception) {
|
||||||
|
SpiderDebug.log("登录失败: " + e.message)
|
||||||
|
Notify.show("123登录失败: " + e.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun startFlow() {
|
||||||
|
Init.run { this.showInput() }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun showInput() {
|
||||||
|
try {
|
||||||
|
val margin = ResUtil.dp2px(16)
|
||||||
|
val params =
|
||||||
|
LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
|
||||||
|
val frame = LinearLayout(Init.context())
|
||||||
|
frame.setOrientation(LinearLayout.VERTICAL)
|
||||||
|
// frame.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||||
|
// params.setMargins(margin, margin, margin, margin);
|
||||||
|
val username = EditText(Init.context())
|
||||||
|
username.setHint("请输入123用户名")
|
||||||
|
val password = EditText(Init.context())
|
||||||
|
password.setHint("请输入123密码")
|
||||||
|
frame.addView(username, params)
|
||||||
|
frame.addView(password, params)
|
||||||
|
dialog = AlertDialog.Builder(Init.getActivity()).setTitle("请输入123用户名和密码").setView(frame)
|
||||||
|
.setNegativeButton(
|
||||||
|
R.string.cancel, null
|
||||||
|
).setPositiveButton(
|
||||||
|
R.string.ok, DialogInterface.OnClickListener { dialog: DialogInterface?, which: Int ->
|
||||||
|
onPositive(
|
||||||
|
username.getText().toString(), password.getText().toString()
|
||||||
|
)
|
||||||
|
}).show()
|
||||||
|
} catch (ignored: Exception) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun onPositive(username: String?, password: String?) {
|
||||||
|
SpiderDebug.log("123用户名: $username")
|
||||||
|
SpiderDebug.log("123密码: $password")
|
||||||
|
dismiss()
|
||||||
|
Init.execute(Runnable {
|
||||||
|
loginWithPassword(username, password)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun dismiss() {
|
||||||
|
try {
|
||||||
|
if (dialog != null) dialog!!.dismiss()
|
||||||
|
} catch (ignored: Exception) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,726 @@
|
||||||
|
package com.github.catvod.api;
|
||||||
|
|
||||||
|
import android.app.AlertDialog;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.graphics.drawable.ColorDrawable;
|
||||||
|
import android.os.SystemClock;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.view.Gravity;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.FrameLayout;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import com.github.catvod.bean.Result;
|
||||||
|
import com.github.catvod.bean.Vod;
|
||||||
|
import com.github.catvod.bean.quark.Cache;
|
||||||
|
import com.github.catvod.bean.quark.Item;
|
||||||
|
import com.github.catvod.bean.quark.ShareData;
|
||||||
|
import com.github.catvod.bean.quark.User;
|
||||||
|
import com.github.catvod.crawler.SpiderDebug;
|
||||||
|
import com.github.catvod.net.OkHttp;
|
||||||
|
import com.github.catvod.net.OkResult;
|
||||||
|
import com.github.catvod.spider.Init;
|
||||||
|
import com.github.catvod.spider.Proxy;
|
||||||
|
import com.github.catvod.utils.*;
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
public class QuarkApi {
|
||||||
|
private String apiUrl = "https://drive-pc.quark.cn/1/clouddrive/";
|
||||||
|
private String cookie = "";
|
||||||
|
private String ckey = "";
|
||||||
|
private Map<String, Map<String, Object>> shareTokenCache = new HashMap<>();
|
||||||
|
private String pr = "pr=ucpro&fr=pc";
|
||||||
|
private List<String> subtitleExts = Arrays.asList(".srt", ".ass", ".scc", ".stl", ".ttml");
|
||||||
|
private Map<String, String> saveFileIdCaches = new HashMap<>();
|
||||||
|
private String saveDirId = null;
|
||||||
|
private String saveDirName = "TV";
|
||||||
|
private boolean isVip = false;
|
||||||
|
private final Cache cache;
|
||||||
|
private ScheduledExecutorService service;
|
||||||
|
|
||||||
|
|
||||||
|
private AlertDialog dialog;
|
||||||
|
private String serviceTicket;
|
||||||
|
|
||||||
|
public Object[] proxyVideo(Map<String, String> params) throws Exception {
|
||||||
|
String url = Util.base64Decode(params.get("url"));
|
||||||
|
Map header = new Gson().fromJson(Util.base64Decode(params.get("header")), Map.class);
|
||||||
|
if (header == null) header = new HashMap<>();
|
||||||
|
List<String> arr = List.of("Range", "Accept", "Accept-Encoding", "Accept-Language", "Cookie", "Origin", "Referer", "Sec-Ch-Ua", "Sec-Ch-Ua-Mobile", "Sec-Ch-Ua-Platform", "Sec-Fetch-Dest", "Sec-Fetch-Mode", "Sec-Fetch-Site", "User-Agent");
|
||||||
|
for (String key : params.keySet()) {
|
||||||
|
for (String s : arr) {
|
||||||
|
if (s.toLowerCase().equals(key.toLowerCase())) {
|
||||||
|
header.put(key, params.get(key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if (Util.getExt(url).contains("m3u8")) {
|
||||||
|
return getM3u8(url, header);
|
||||||
|
}
|
||||||
|
return ProxyVideo.proxyMultiThread(url, header);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 代理m3u8
|
||||||
|
*
|
||||||
|
* @param url
|
||||||
|
* @param header
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private Object[] getM3u8(String url, Map header) {
|
||||||
|
|
||||||
|
OkResult result = OkHttp.get(url, new HashMap<>(), header);
|
||||||
|
String[] m3u8Arr = result.getBody().split("\n");
|
||||||
|
List<String> listM3u8 = new ArrayList<>();
|
||||||
|
|
||||||
|
String site = url.substring(0, url.lastIndexOf("/")) + "/";
|
||||||
|
int mediaId = 0;
|
||||||
|
for (String oneLine : m3u8Arr) {
|
||||||
|
String thisOne = oneLine;
|
||||||
|
if (oneLine.contains(".ts")) {
|
||||||
|
thisOne = proxyVideoUrl(site + thisOne, header);
|
||||||
|
mediaId++;
|
||||||
|
}
|
||||||
|
listM3u8.add(thisOne);
|
||||||
|
}
|
||||||
|
String m3u8Str = TextUtils.join("\n", listM3u8);
|
||||||
|
String contentType = result.getResp().get("Content-Type").get(0);
|
||||||
|
|
||||||
|
Map<String, String> respHeaders = new HashMap<>();
|
||||||
|
for (String key : result.getResp().keySet()) {
|
||||||
|
respHeaders.put(key, result.getResp().get(key).get(0));
|
||||||
|
}
|
||||||
|
return new Object[]{result.getCode(), contentType, new ByteArrayInputStream(m3u8Str.getBytes(Charset.forName("UTF-8"))), respHeaders};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Loader {
|
||||||
|
static volatile QuarkApi INSTANCE = new QuarkApi();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static QuarkApi get() {
|
||||||
|
return QuarkApi.Loader.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCookie(String token) throws Exception {
|
||||||
|
if (StringUtils.isNoneBlank(token)) {
|
||||||
|
this.cookie = token;
|
||||||
|
initUserInfo();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, String> getHeaders() {
|
||||||
|
Map<String, String> headers = new HashMap<>();
|
||||||
|
headers.put("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) quark-cloud-drive/2.5.20 Chrome/100.0.4896.160 Electron/18.3.5.4-b478491100 Safari/537.36 Channel/pckk_other_ch");
|
||||||
|
headers.put("Referer", "https://pan.quark.cn/");
|
||||||
|
headers.put("Content-Type", "application/json");
|
||||||
|
headers.put("Cookie", cookie);
|
||||||
|
headers.put("Host", "drive-pc.quark.cn");
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, String> getWebHeaders() {
|
||||||
|
Map<String, String> headers = new HashMap<>();
|
||||||
|
headers.put("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) quark-cloud-drive/2.5.20 Chrome/100.0.4896.160 Electron/18.3.5.4-b478491100 Safari/537.36 Channel/pckk_other_ch");
|
||||||
|
headers.put("Referer", "https://pan.quark.cn/");
|
||||||
|
headers.put("Cookie", cookie);
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void initQuark(String cookie) throws Exception {
|
||||||
|
this.ckey = Util.MD5(cookie);
|
||||||
|
this.cookie = cookie;
|
||||||
|
this.isVip = getVip();
|
||||||
|
}
|
||||||
|
|
||||||
|
private QuarkApi() {
|
||||||
|
Init.checkPermission();
|
||||||
|
|
||||||
|
cache = Cache.objectFrom(Path.read(getCache()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public File getCache() {
|
||||||
|
return Path.tv("quark");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vod getVod(ShareData shareData) throws Exception {
|
||||||
|
getShareToken(shareData);
|
||||||
|
List<Item> files = new ArrayList<>();
|
||||||
|
List<Item> subs = new ArrayList<>();
|
||||||
|
try {
|
||||||
|
List<Map<String, Object>> listData = listFile(1, shareData, files, subs, shareData.getShareId(), shareData.getFolderId(), 1);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
SpiderDebug.log("资源已取消:" + e.getMessage());
|
||||||
|
Notify.show("资源已取消");
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> playFrom = QuarkApi.get().getPlayFormatList();
|
||||||
|
|
||||||
|
List<String> playFromtmp = new ArrayList<>();
|
||||||
|
playFromtmp.add("quark原画");
|
||||||
|
for (String s : playFrom) {
|
||||||
|
playFromtmp.add("quark" + s);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> playUrl = new ArrayList<>();
|
||||||
|
|
||||||
|
if (files.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < files.get(files.size() - 1).getShareIndex(); i++) {
|
||||||
|
for (int index = 0; index < playFromtmp.size(); index++) {
|
||||||
|
List<String> vodItems = new ArrayList<>();
|
||||||
|
for (Item video_item : files) {
|
||||||
|
if (video_item.getShareIndex() == i + 1) {
|
||||||
|
vodItems.add(video_item.getEpisodeUrl("电影"));// + findSubs(video_item.getName(), subs));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
playUrl.add(TextUtils.join("#", vodItems));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Vod vod = new Vod();
|
||||||
|
vod.setVodId("");
|
||||||
|
vod.setVodContent("");
|
||||||
|
vod.setVodPic("");
|
||||||
|
vod.setVodName("");
|
||||||
|
vod.setVodPlayUrl(TextUtils.join("$$$", playUrl));
|
||||||
|
vod.setVodPlayFrom(TextUtils.join("$$$", playFromtmp));
|
||||||
|
vod.setTypeName("夸克云盘");
|
||||||
|
return vod;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String playerContent(String[] split, String flag) throws Exception {
|
||||||
|
|
||||||
|
String fileId = split[0], fileToken = split[1], shareId = split[2], stoken = split[3];
|
||||||
|
String playUrl = "";
|
||||||
|
Map<String, String> header = getHeaders();
|
||||||
|
header.remove("Host");
|
||||||
|
header.remove("Content-Type");
|
||||||
|
if (flag.contains("quark原画")) {
|
||||||
|
playUrl = this.getDownload(shareId, stoken, fileId, fileToken, true);
|
||||||
|
return Result.get().url(ProxyServer.INSTANCE.buildProxyUrl(playUrl, header)).octet().header(header).string();
|
||||||
|
} else {
|
||||||
|
playUrl = this.getLiveTranscoding(shareId, stoken, fileId, fileToken, flag);
|
||||||
|
return Result.get().url(proxyVideoUrl(playUrl, header)).octet().header(header).string();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private String proxyVideoUrl(String url, Map<String, String> header) {
|
||||||
|
return String.format(Proxy.getUrl() + "?do=quark&type=video&url=%s&header=%s", Util.base64Encode(url.getBytes(Charset.defaultCharset())), Util.base64Encode(Json.toJson(header).getBytes(Charset.defaultCharset())));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param url
|
||||||
|
* @param params get 参数
|
||||||
|
* @param data post json
|
||||||
|
* @param retry
|
||||||
|
* @param method
|
||||||
|
* @return
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
private String api(String url, Map<String, String> params, Map<String, Object> data, Integer retry, String method) throws Exception {
|
||||||
|
|
||||||
|
|
||||||
|
int leftRetry = retry != null ? retry : 3;
|
||||||
|
if (StringUtils.isAllBlank(cookie)) {
|
||||||
|
this.initUserInfo();
|
||||||
|
return api(url, params, data, leftRetry - 1, method);
|
||||||
|
}
|
||||||
|
OkResult okResult;
|
||||||
|
if ("GET".equals(method)) {
|
||||||
|
okResult = OkHttp.get(this.apiUrl + url, params, getHeaders());
|
||||||
|
} else {
|
||||||
|
okResult = OkHttp.post(this.apiUrl + url, Json.toJson(data), getHeaders());
|
||||||
|
}
|
||||||
|
if (okResult.getResp().get("Set-Cookie") != null) {
|
||||||
|
Matcher matcher = Pattern.compile("__puus=([^;]+)").matcher(StringUtils.join(okResult.getResp().get("Set-Cookie"), ";;;"));
|
||||||
|
if (matcher.find()) {
|
||||||
|
Matcher cookieMatcher = Pattern.compile("__puus=([^;]+)").matcher(this.cookie);
|
||||||
|
if (cookieMatcher.find() && !cookieMatcher.group(1).equals(matcher.group(1))) {
|
||||||
|
this.cookie = this.cookie.replaceAll("__puus=[^;]+", "__puus=" + matcher.group(1));
|
||||||
|
} else {
|
||||||
|
this.cookie = this.cookie + ";__puus=" + matcher.group(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (okResult.getCode() != 200 && leftRetry > 0) {
|
||||||
|
SpiderDebug.log("api error code:" + okResult.getCode());
|
||||||
|
Thread.sleep(1000);
|
||||||
|
return api(url, params, data, leftRetry - 1, method);
|
||||||
|
}
|
||||||
|
return okResult.getBody();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initUserInfo() {
|
||||||
|
try {
|
||||||
|
SpiderDebug.log("initUserInfo...");
|
||||||
|
|
||||||
|
//extend没有cookie,从缓存中获取
|
||||||
|
if (StringUtils.isAllBlank(cookie)) {
|
||||||
|
SpiderDebug.log(" cookie from ext is empty...");
|
||||||
|
cookie = cache.getUser().getCookie();
|
||||||
|
}
|
||||||
|
//获取到cookie,初始化quark,并且把cookie缓存一次
|
||||||
|
if (StringUtils.isNoneBlank(cookie) && cookie.contains("__pus")) {
|
||||||
|
SpiderDebug.log(" initQuark ...");
|
||||||
|
initQuark(this.cookie);
|
||||||
|
cache.setUser(User.objectFrom(this.cookie));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//没有cookie,也没有serviceTicket,抛出异常,提示用户重新登录
|
||||||
|
if (StringUtils.isAllBlank(cookie) && StringUtils.isAllBlank(serviceTicket)) {
|
||||||
|
SpiderDebug.log("cookie为空");
|
||||||
|
throw new RuntimeException("cookie为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
String token = serviceTicket;
|
||||||
|
OkResult result = OkHttp.get("https://pan.quark.cn/account/info?st=" + token + "&lw=scan", new HashMap<>(), getWebHeaders());
|
||||||
|
Map json = Json.parseSafe(result.getBody(), Map.class);
|
||||||
|
if (json.get("success").equals(Boolean.TRUE)) {
|
||||||
|
List<String> cookies = result.getResp().get("set-Cookie");
|
||||||
|
List<String> cookieList = new ArrayList<>();
|
||||||
|
for (String cookie : cookies) {
|
||||||
|
cookieList.add(cookie.split(";")[0]);
|
||||||
|
}
|
||||||
|
this.cookie += TextUtils.join(";", cookieList);
|
||||||
|
|
||||||
|
cache.setUser(User.objectFrom(this.cookie));
|
||||||
|
if (cache.getUser().getCookie().isEmpty()) throw new Exception(this.cookie);
|
||||||
|
initQuark(this.cookie);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
cache.getUser().clean();
|
||||||
|
e.printStackTrace();
|
||||||
|
stopService();
|
||||||
|
startFlow();
|
||||||
|
} finally {
|
||||||
|
while (cache.getUser().getCookie().isEmpty()) SystemClock.sleep(250);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取二维码登录的令牌
|
||||||
|
*
|
||||||
|
* @return 返回包含二维码登录令牌的字符串
|
||||||
|
*/
|
||||||
|
private String getTokenForQrcodeLogin() {
|
||||||
|
Map<String, String> params = new HashMap<>();
|
||||||
|
params.put("client_id", "386");
|
||||||
|
params.put("v", "1.2");
|
||||||
|
params.put("request_id", UUID.randomUUID().toString());
|
||||||
|
OkResult res = OkHttp.get("https://uop.quark.cn/cas/ajax/getTokenForQrcodeLogin", params, new HashMap<>());
|
||||||
|
if (this.cookie.isEmpty()) {
|
||||||
|
List<String> cookies = res.getResp().get("set-Cookie");
|
||||||
|
List<String> cookieList = new ArrayList<>();
|
||||||
|
for (String cookie : cookies) {
|
||||||
|
cookieList.add(cookie.split(";")[0]);
|
||||||
|
}
|
||||||
|
this.cookie = TextUtils.join(";", cookieList);
|
||||||
|
}
|
||||||
|
Map<String, Object> json = Json.parseSafe(res.getBody(), Map.class);
|
||||||
|
if (Objects.equals(json.get("message"), "ok")) {
|
||||||
|
return (String) ((Map<String, Object>) ((Map<String, Object>) json.get("data")).get("members")).get("token");
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取二维码内容
|
||||||
|
* <p>
|
||||||
|
* 此方法用于生成二维码的URL内容该URL用于二维码登录,包含了登录所需的token和客户端信息
|
||||||
|
*
|
||||||
|
* @return 返回包含token的二维码URL字符串
|
||||||
|
*/
|
||||||
|
private String getQrCodeToken() {
|
||||||
|
// 获取用于二维码登录的token
|
||||||
|
String token = getTokenForQrcodeLogin();
|
||||||
|
// 组装二维码URL,包含token和客户端标识
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public ShareData getShareData(String url) {
|
||||||
|
Pattern pattern = Pattern.compile("https://pan\\.quark\\.cn/s/([^\\\\|#/]+)");
|
||||||
|
Matcher matcher = pattern.matcher(url);
|
||||||
|
if (matcher.find()) {
|
||||||
|
return new ShareData(matcher.group(1), "0");
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean getVip() throws Exception {
|
||||||
|
Map<String, Object> listData = Json.parseSafe(api("member?pr=ucpro&fr=pc&uc_param_str=&fetch_subscribe=true&_ch=home&fetch_identity=true", null, null, 0, "GET"), Map.class);
|
||||||
|
return ((Map<String, String>) listData.get("data")).get("member_type").contains("VIP");
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getPlayFormatList() {
|
||||||
|
if (this.isVip) {
|
||||||
|
return Arrays.asList("4K"/*, "超清", "高清", "普画"*/);
|
||||||
|
} else {
|
||||||
|
return Collections.singletonList("普画");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> getPlayFormatQuarkList() {
|
||||||
|
if (this.isVip) {
|
||||||
|
return Arrays.asList("4k", "2k", "super", "high", "normal", "low");
|
||||||
|
} else {
|
||||||
|
return Collections.singletonList("low");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void getShareToken(ShareData shareData) throws Exception {
|
||||||
|
if (!this.shareTokenCache.containsKey(shareData.getShareId())) {
|
||||||
|
this.shareTokenCache.remove(shareData.getShareId());
|
||||||
|
Map<String, Object> shareToken = Json.parseSafe(api("share/sharepage/token?" + this.pr, Collections.emptyMap(), Map.of("pwd_id", shareData.getShareId(), "passcode", shareData.getSharePwd() == null ? "" : shareData.getSharePwd()), 0, "POST"), Map.class);
|
||||||
|
if (shareToken.containsKey("data") && ((Map<String, Object>) shareToken.get("data")).containsKey("stoken")) {
|
||||||
|
this.shareTokenCache.put(shareData.getShareId(), (Map<String, Object>) shareToken.get("data"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Map<String, Object>> listFile(int shareIndex, ShareData shareData, List<Item> videos, List<Item> subtitles, String shareId, String folderId, Integer page) throws Exception {
|
||||||
|
int prePage = 200;
|
||||||
|
page = page != null ? page : 1;
|
||||||
|
|
||||||
|
Map<String, Object> listData = Json.parseSafe(api("share/sharepage/detail?" + this.pr + "&pwd_id=" + shareId + "&stoken=" + encodeURIComponent((String) this.shareTokenCache.get(shareId).get("stoken")) + "&pdir_fid=" + folderId + "&force=0&_page=" + page + "&_size=" + prePage + "&_sort=file_type:asc,file_name:asc", Collections.emptyMap(), Collections.emptyMap(), 0, "GET"), Map.class);
|
||||||
|
if (listData.get("data") == null) return Collections.emptyList();
|
||||||
|
List<Map<String, Object>> items = (List<Map<String, Object>>) ((Map<String, Object>) listData.get("data")).get("list");
|
||||||
|
if (items == null) return Collections.emptyList();
|
||||||
|
List<Map<String, Object>> subDir = new ArrayList<>();
|
||||||
|
for (Map<String, Object> item : items) {
|
||||||
|
if (Boolean.TRUE.equals(item.get("dir"))) {
|
||||||
|
subDir.add(item);
|
||||||
|
} else if (Boolean.TRUE.equals(item.get("file")) && (Util.isMedia((String) item.get("file_name")))) {
|
||||||
|
if ((Double) item.get("size") < 1024 * 1024 * 5) continue;
|
||||||
|
item.put("stoken", this.shareTokenCache.get(shareData.getShareId()).get("stoken"));
|
||||||
|
videos.add(Item.objectFrom(item, shareData.getShareId(), shareIndex));
|
||||||
|
} else if ("file".equals(item.get("type")) && this.subtitleExts.contains("." + Util.getExt((String) item.get("file_name")))) {
|
||||||
|
subtitles.add(Item.objectFrom(item, shareData.getShareId(), shareIndex));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (page < Math.ceil((double) ((Map<String, Object>) listData.get("metadata")).get("_total") / prePage)) {
|
||||||
|
List<Map<String, Object>> nextItems = listFile(shareIndex, shareData, videos, subtitles, shareId, folderId, page + 1);
|
||||||
|
items.addAll(nextItems);
|
||||||
|
}
|
||||||
|
for (Map<String, Object> dir : subDir) {
|
||||||
|
List<Map<String, Object>> subItems = listFile(shareIndex, shareData, videos, subtitles, shareId, dir.get("fid").toString(), null);
|
||||||
|
items.addAll(subItems);
|
||||||
|
}
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, Object> findBestLCS(Item mainItem, List<Item> targetItems) {
|
||||||
|
List<Map<String, Object>> results = new ArrayList<>();
|
||||||
|
int bestMatchIndex = 0;
|
||||||
|
for (int i = 0; i < targetItems.size(); i++) {
|
||||||
|
Util.LCSResult currentLCS = Util.lcs(mainItem.getName(), targetItems.get(i).getName());
|
||||||
|
Map<String, Object> result = new HashMap<>();
|
||||||
|
result.put("target", targetItems.get(i));
|
||||||
|
result.put("lcs", currentLCS);
|
||||||
|
results.add(result);
|
||||||
|
if (currentLCS.length > results.get(bestMatchIndex).get("lcs").toString().length()) {
|
||||||
|
bestMatchIndex = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Map<String, Object> bestMatch = results.get(bestMatchIndex);
|
||||||
|
Map<String, Object> finalResult = new HashMap<>();
|
||||||
|
finalResult.put("allLCS", results);
|
||||||
|
finalResult.put("bestMatch", bestMatch);
|
||||||
|
finalResult.put("bestMatchIndex", bestMatchIndex);
|
||||||
|
return finalResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void getFilesByShareUrl(int shareIndex, String shareInfo, List<Item> videos, List<Item> subtitles) throws Exception {
|
||||||
|
ShareData shareData = getShareData((String) shareInfo);
|
||||||
|
if (shareData == null) return;
|
||||||
|
getShareToken(shareData);
|
||||||
|
if (!this.shareTokenCache.containsKey(shareData.getShareId())) return;
|
||||||
|
listFile(shareIndex, shareData, videos, subtitles, shareData.getShareId(), shareData.getFolderId(), 1);
|
||||||
|
if (!subtitles.isEmpty()) {
|
||||||
|
for (Item video : videos) {
|
||||||
|
Map<String, Object> matchSubtitle = findBestLCS(video, subtitles);
|
||||||
|
if (matchSubtitle.get("bestMatch") != null) {
|
||||||
|
video.setSubtitle((String) ((Map<String, Object>) matchSubtitle.get("bestMatch")).get("target"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void clean() {
|
||||||
|
saveFileIdCaches.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void clearSaveDir() throws Exception {
|
||||||
|
Map<String, Object> listData = Json.parseSafe(api("file/sort?" + this.pr + "&pdir_fid=" + this.saveDirId + "&_page=1&_size=200&_sort=file_type:asc,updated_at:desc", Collections.emptyMap(), Collections.emptyMap(), 0, "GET"), Map.class);
|
||||||
|
|
||||||
|
if (listData.get("data") != null) {
|
||||||
|
List<Map<String, Object>> fileList = (List<Map<String, Object>>) ((Map<String, Object>) listData.get("data")).get("list");
|
||||||
|
if (fileList.size() >= 10) {
|
||||||
|
List<String> fileIdsToDelete = new ArrayList<>();
|
||||||
|
for (Map<String, Object> file : fileList) {
|
||||||
|
fileIdsToDelete.add((String) file.get("fid"));
|
||||||
|
}
|
||||||
|
api("file/delete?" + this.pr + "&uc_param_str=", Collections.emptyMap(), Map.of("action_type", 2, "filelist", fileIdsToDelete, "exclude_fids", Collections.emptyList()), 0, "POST");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createSaveDir(boolean clean) throws Exception {
|
||||||
|
if (this.saveDirId != null) {
|
||||||
|
if (clean) clearSaveDir();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Object> listData = Json.parseSafe(api("file/sort?" + this.pr + "&pdir_fid=0&_page=1&_size=200&_sort=file_type:asc,updated_at:desc", Collections.emptyMap(), Collections.emptyMap(), 0, "GET"), Map.class);
|
||||||
|
if (listData.get("data") != null) {
|
||||||
|
for (Map<String, Object> item : (List<Map<String, Object>>) ((Map<String, Object>) listData.get("data")).get("list")) {
|
||||||
|
if (this.saveDirName.equals(item.get("file_name"))) {
|
||||||
|
this.saveDirId = item.get("fid").toString();
|
||||||
|
clearSaveDir();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.saveDirId == null) {
|
||||||
|
Map<String, Object> create = Json.parseSafe(api("file?" + this.pr, Collections.emptyMap(), Map.of("pdir_fid", "0", "file_name", this.saveDirName, "dir_path", "", "dir_init_lock", "false"), 0, "POST"), Map.class);
|
||||||
|
if (create.get("data") != null && ((Map<String, Object>) create.get("data")).get("fid") != null) {
|
||||||
|
this.saveDirId = ((Map<String, Object>) create.get("data")).get("fid").toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String save(String shareId, String stoken, String fileId, String fileToken, boolean clean) throws Exception {
|
||||||
|
createSaveDir(clean);
|
||||||
|
if (clean) {
|
||||||
|
clean();
|
||||||
|
}
|
||||||
|
if (this.saveDirId == null) return null;
|
||||||
|
if (stoken == null) {
|
||||||
|
getShareToken(new ShareData(shareId, null));
|
||||||
|
if (!this.shareTokenCache.containsKey(shareId)) return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Object> saveResult = Json.parseSafe(api("share/sharepage/save?" + this.pr, null, Map.of("fid_list", List.of(fileId), "fid_token_list", List.of(fileToken), "to_pdir_fid", this.saveDirId, "pwd_id", shareId, "stoken", stoken != null ? stoken : (String) this.shareTokenCache.get(shareId).get("stoken"), "pdir_fid", "0", "scene", "link"), 0, "POST"), Map.class);
|
||||||
|
if (saveResult.get("data") != null && ((Map<Object, Object>) saveResult.get("data")).get("task_id") != null) {
|
||||||
|
int retry = 0;
|
||||||
|
while (true) {
|
||||||
|
|
||||||
|
Map<String, Object> taskResult = Json.parseSafe(api("task?" + this.pr + "&task_id=" + ((Map<String, Object>) saveResult.get("data")).get("task_id") + "&retry_index=" + retry, Collections.emptyMap(), Collections.emptyMap(), 0, "GET"), Map.class);
|
||||||
|
if (taskResult.get("data") != null && ((Map<Object, Object>) taskResult.get("data")).get("save_as") != null && ((Map<Object, Object>) ((Map<Object, Object>) taskResult.get("data")).get("save_as")).get("save_as_top_fids") != null && ((List<String>) ((Map<String, Object>) ((Map<String, Object>) taskResult.get("data")).get("save_as")).get("save_as_top_fids")).size() > 0) {
|
||||||
|
return ((List<String>) ((Map<String, Object>) ((Map<Object, Object>) taskResult.get("data")).get("save_as")).get("save_as_top_fids")).get(0);
|
||||||
|
}
|
||||||
|
retry++;
|
||||||
|
if (retry > 2) break;
|
||||||
|
Thread.sleep(1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getLiveTranscoding(String shareId, String stoken, String fileId, String fileToken, String flag) throws Exception {
|
||||||
|
if (!this.saveFileIdCaches.containsKey(fileId)) {
|
||||||
|
String saveFileId = save(shareId, stoken, fileId, fileToken, true);
|
||||||
|
if (saveFileId == null) return null;
|
||||||
|
this.saveFileIdCaches.put(fileId, saveFileId);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Object> transcoding = Json.parseSafe(api("file/v2/play?" + this.pr, Collections.emptyMap(), Map.of("fid", this.saveFileIdCaches.get(fileId), "resolutions", "normal,low,high,super,2k,4k", "supports", "fmp4"), 0, "POST"), Map.class);
|
||||||
|
if (transcoding.get("data") != null && ((Map<Object, Object>) transcoding.get("data")).get("video_list") != null) {
|
||||||
|
String flagId = flag.split("-")[flag.split("-").length - 1];
|
||||||
|
int index = Util.findAllIndexes(getPlayFormatList(), flagId);
|
||||||
|
String quarkFormat = getPlayFormatQuarkList().get(index);
|
||||||
|
for (Map<String, Object> video : (List<Map<String, Object>>) ((Map<Object, Object>) transcoding.get("data")).get("video_list")) {
|
||||||
|
if (video.get("resolution").equals(quarkFormat)) {
|
||||||
|
return (String) ((Map<String, Object>) video.get("video_info")).get("url");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (String) ((Map<String, Object>) ((List<Map<String, Object>>) ((Map<Object, Object>) transcoding.get("data")).get("video_list")).get(index).get("video_info")).get("url");
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getDownload(String shareId, String stoken, String fileId, String fileToken, boolean clean) throws Exception {
|
||||||
|
if (!this.saveFileIdCaches.containsKey(fileId)) {
|
||||||
|
String saveFileId = save(shareId, stoken, fileId, fileToken, clean);
|
||||||
|
if (saveFileId == null) return null;
|
||||||
|
this.saveFileIdCaches.put(fileId, saveFileId);
|
||||||
|
}
|
||||||
|
Map<String, Object> down = Json.parseSafe(api("file/download?" + this.pr + "&uc_param_str=", Collections.emptyMap(), Map.of("fids", List.of(this.saveFileIdCaches.get(fileId))), 0, "POST"), Map.class);
|
||||||
|
if (down.get("data") != null) {
|
||||||
|
return ((List<Map<String, Object>>) down.get("data")).get(0).get("download_url").toString();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper method to convert bytes to hex string
|
||||||
|
private String bytesToHex(byte[] bytes) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (byte b : bytes) {
|
||||||
|
sb.append(String.format("%02x", b));
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encoding helper method
|
||||||
|
private String encodeURIComponent(String value) {
|
||||||
|
try {
|
||||||
|
return java.net.URLEncoder.encode(value, "UTF-8");
|
||||||
|
} catch (Exception e) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startFlow() {
|
||||||
|
Init.run(this::showInput);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showInput() {
|
||||||
|
try {
|
||||||
|
int margin = ResUtil.dp2px(16);
|
||||||
|
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
|
FrameLayout frame = new FrameLayout(Init.context());
|
||||||
|
params.setMargins(margin, margin, margin, margin);
|
||||||
|
EditText input = new EditText(Init.context());
|
||||||
|
frame.addView(input, params);
|
||||||
|
dialog = new AlertDialog.Builder(Init.getActivity()).setTitle("请输入cookie").setView(frame).setNeutralButton("夸克二维码", (dialog, which) -> onNeutral()).setNegativeButton(android.R.string.cancel, null).setPositiveButton(android.R.string.ok, (dialog, which) -> onPositive(input.getText().toString())).show();
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onNeutral() {
|
||||||
|
dismiss();
|
||||||
|
Init.execute(this::getQRCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onPositive(String text) {
|
||||||
|
dismiss();
|
||||||
|
Init.execute(() -> {
|
||||||
|
if (text.startsWith("http")) setToken(OkHttp.string(text));
|
||||||
|
else setToken(text);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void getQRCode() {
|
||||||
|
String token = getQrCodeToken();
|
||||||
|
|
||||||
|
Init.run(() -> openApp(token));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void openApp(String token) {
|
||||||
|
try {
|
||||||
|
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||||
|
intent.setClassName("com.alicloud.databox", "com.taobao.login4android.scan.QrScanActivity");
|
||||||
|
intent.putExtra("key_scanParam", token);
|
||||||
|
Init.getActivity().startActivity(intent);
|
||||||
|
} catch (Exception e) {
|
||||||
|
showQRCode("https://su.quark.cn/4_eMHBJ?uc_param_str=&token=" + token + "&client_id=532&uc_biz_str=S%3Acustom%7COPT%3ASAREA%400%7COPT%3AIMMERSIVE%401%7COPT%3ABACK_BTN_STYLE%400");
|
||||||
|
} finally {
|
||||||
|
Map<String, String> map = new HashMap<>();
|
||||||
|
map.put("token", token);
|
||||||
|
Init.execute(() -> startService(map));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showQRCode(String content) {
|
||||||
|
try {
|
||||||
|
int size = ResUtil.dp2px(240);
|
||||||
|
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(size, size);
|
||||||
|
ImageView image = new ImageView(Init.context());
|
||||||
|
image.setScaleType(ImageView.ScaleType.CENTER_CROP);
|
||||||
|
image.setImageBitmap(QRCode.getBitmap(content, size, 2));
|
||||||
|
FrameLayout frame = new FrameLayout(Init.context());
|
||||||
|
params.gravity = Gravity.CENTER;
|
||||||
|
frame.addView(image, params);
|
||||||
|
dialog = new AlertDialog.Builder(Init.getActivity()).setView(frame).setOnCancelListener(this::dismiss).setOnDismissListener(this::dismiss).show();
|
||||||
|
dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
|
||||||
|
Notify.show("请使用夸克网盘App扫描二维码");
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startService(Map<String, String> params) {
|
||||||
|
SpiderDebug.log("----startservice");
|
||||||
|
params.put("client_id", "532");
|
||||||
|
params.put("v", "1.2");
|
||||||
|
params.put("request_id", UUID.randomUUID().toString());
|
||||||
|
service = Executors.newScheduledThreadPool(1);
|
||||||
|
/* timer = new Timer(true);
|
||||||
|
TimerTask task = new TimerTask()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
SpiderDebug.log("----scheduleAtFixedRate"+new Date().toString());
|
||||||
|
String result = OkHttp.string("https://uop.quark.cn/cas/ajax/getServiceTicketByQrcodeToken", params, getWebHeaders());
|
||||||
|
Map<String,Object> json = Json.parseSafe(result, Map.class);
|
||||||
|
if (json.get("status").equals(2000000)) {
|
||||||
|
setToken((String) ((Map<String,Object>)((Map<String,Object>)json.get("data")).get("members")).get("service_ticket"));
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
timer.schedule(task, 1000, 2000);*/
|
||||||
|
|
||||||
|
service.scheduleWithFixedDelay(() -> {
|
||||||
|
SpiderDebug.log("----scheduleAtFixedRate" + new Date().toString());
|
||||||
|
String result = OkHttp.string("https://uop.quark.cn/cas/ajax/getServiceTicketByQrcodeToken", params, getWebHeaders());
|
||||||
|
Map<String, Object> json = Json.parseSafe(result, Map.class);
|
||||||
|
if (json.get("status").equals(new Double(2000000))) {
|
||||||
|
setToken((String) ((Map<String, Object>) ((Map<String, Object>) json.get("data")).get("members")).get("service_ticket"));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}, 1, 3, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setToken(String value) {
|
||||||
|
this.serviceTicket = value;
|
||||||
|
SpiderDebug.log("ServiceTicket:" + value);
|
||||||
|
Notify.show("ServiceTicket:" + value);
|
||||||
|
initUserInfo();
|
||||||
|
stopService();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void stopService() {
|
||||||
|
if (service != null) service.shutdownNow();
|
||||||
|
|
||||||
|
|
||||||
|
Init.run(this::dismiss);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void dismiss(DialogInterface dialog) {
|
||||||
|
stopService();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void dismiss() {
|
||||||
|
try {
|
||||||
|
if (dialog != null) dialog.dismiss();
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,106 @@
|
||||||
|
package com.github.catvod.api;
|
||||||
|
|
||||||
|
|
||||||
|
import com.github.catvod.bean.tianyi.Cache;
|
||||||
|
import com.github.catvod.bean.tianyi.User;
|
||||||
|
import com.github.catvod.crawler.SpiderDebug;
|
||||||
|
import com.github.catvod.utils.Json;
|
||||||
|
import com.github.catvod.utils.Path;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import okhttp3.HttpUrl;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class SimpleCookieJar {
|
||||||
|
private Map<String, Map<String, String>> cookieStore = new HashMap<>();
|
||||||
|
|
||||||
|
|
||||||
|
public Map<String, Map<String, String>> getCookieStore() {
|
||||||
|
return cookieStore;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCookieStore(Map<String, Map<String, String>> cookieStore) {
|
||||||
|
this.cookieStore = cookieStore;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SimpleCookieJar() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public void saveFromResponse(String url, List<String> cookies) {
|
||||||
|
HttpUrl httpUrl = HttpUrl.parse(url);
|
||||||
|
SpiderDebug.log(" saveFromResponse url: " + url);
|
||||||
|
SpiderDebug.log(" saveFromResponse cookie : " + Json.toJson(cookies));
|
||||||
|
// 创建可修改的 Cookie 列表副本
|
||||||
|
Map<String, String> oldCookies = cookieStore.get(httpUrl.host()) != null ? cookieStore.get(httpUrl.host()) : new HashMap<>();
|
||||||
|
|
||||||
|
// 更新 Cookie
|
||||||
|
for (String newCookie : cookies) {
|
||||||
|
String[] split = newCookie.split(";");
|
||||||
|
String cookieItem = split[0].trim();
|
||||||
|
int equalsIndex = cookieItem.indexOf('=');
|
||||||
|
if (equalsIndex > 0) {
|
||||||
|
String key = cookieItem.substring(0, equalsIndex);
|
||||||
|
String value = equalsIndex < cookieItem.length() - 1 ? cookieItem.substring(equalsIndex + 1) : "";
|
||||||
|
if (value.equals("SSON") && StringUtils.isAllBlank(value)) {
|
||||||
|
|
||||||
|
} else {
|
||||||
|
oldCookies.put(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
cookieStore.put(httpUrl.host(), oldCookies);
|
||||||
|
SpiderDebug.log(" cookieStore now: " + Json.toJson(cookieStore));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void setGlobalCookie(JsonObject jsonObject) {
|
||||||
|
|
||||||
|
for (Map.Entry<String, JsonElement> entry : jsonObject.entrySet()) {
|
||||||
|
String key = entry.getKey();
|
||||||
|
JsonObject value = entry.getValue().getAsJsonObject();
|
||||||
|
Map<String, String> cookiesForHost = new HashMap<>();
|
||||||
|
for (String k : value.keySet()) {
|
||||||
|
String cookieobj = value.get(k).getAsString();
|
||||||
|
cookiesForHost.put(k, cookieobj);
|
||||||
|
}
|
||||||
|
cookieStore.put(key, cookiesForHost);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据请求URl获取cookie
|
||||||
|
*
|
||||||
|
* @param url
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String loadForRequest(String url) {
|
||||||
|
HttpUrl httpUrl = HttpUrl.parse(url);
|
||||||
|
Map<String, String> cookieMap = cookieStore.get(httpUrl.host());
|
||||||
|
List<String> cookieList = new ArrayList<>();
|
||||||
|
if (cookieMap != null && cookieMap.size() > 0) {
|
||||||
|
for (String s : cookieMap.keySet()) {
|
||||||
|
cookieList.add(s + "=" + cookieMap.get(s));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String cookie = StringUtils.join(cookieList, ";");
|
||||||
|
SpiderDebug.log(" loadForRequest url:" + url);
|
||||||
|
SpiderDebug.log(" loadForRequest cookie:" + cookie);
|
||||||
|
return cookie;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,627 @@
|
||||||
|
package com.github.catvod.api;
|
||||||
|
|
||||||
|
import android.app.AlertDialog;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.graphics.drawable.ColorDrawable;
|
||||||
|
import android.util.Base64;
|
||||||
|
import android.view.Gravity;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.FrameLayout;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
|
import com.github.catvod.bean.tianyi.Cache;
|
||||||
|
import com.github.catvod.bean.tianyi.User;
|
||||||
|
import com.github.catvod.crawler.SpiderDebug;
|
||||||
|
import com.github.catvod.net.OkHttp;
|
||||||
|
import com.github.catvod.net.OkResult;
|
||||||
|
import com.github.catvod.spider.Init;
|
||||||
|
import com.github.catvod.utils.Json;
|
||||||
|
import com.github.catvod.utils.Notify;
|
||||||
|
import com.github.catvod.utils.Path;
|
||||||
|
import com.github.catvod.utils.QRCode;
|
||||||
|
import com.github.catvod.utils.ResUtil;
|
||||||
|
import com.github.catvod.utils.Util;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.apache.commons.lang3.time.DateFormatUtils;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.security.KeyFactory;
|
||||||
|
import java.security.PublicKey;
|
||||||
|
import java.security.spec.X509EncodedKeySpec;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Random;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
|
||||||
|
import okhttp3.Headers;
|
||||||
|
import okhttp3.HttpUrl;
|
||||||
|
import okhttp3.Request;
|
||||||
|
import okhttp3.Response;
|
||||||
|
|
||||||
|
public class TianYiHandler {
|
||||||
|
|
||||||
|
public static final String API_URL = "https://open.e.189.cn";
|
||||||
|
private ScheduledExecutorService service;
|
||||||
|
private AlertDialog dialog;
|
||||||
|
private Cache cache = null;
|
||||||
|
|
||||||
|
public File getCache() {
|
||||||
|
return Path.tv("tianyi");
|
||||||
|
}
|
||||||
|
|
||||||
|
public File geteCache() {
|
||||||
|
return Path.tv("tianyie");
|
||||||
|
}
|
||||||
|
|
||||||
|
private String indexUrl = "";
|
||||||
|
|
||||||
|
private String reqId;
|
||||||
|
private String lt;
|
||||||
|
|
||||||
|
private SimpleCookieJar cookieJar;
|
||||||
|
|
||||||
|
public SimpleCookieJar getCookieJar() {
|
||||||
|
return cookieJar;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Loader {
|
||||||
|
static volatile TianYiHandler INSTANCE = new TianYiHandler();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TianYiHandler get() {
|
||||||
|
return TianYiHandler.Loader.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private TianYiHandler() {
|
||||||
|
|
||||||
|
cookieJar = new SimpleCookieJar();
|
||||||
|
cache = Cache.objectFrom(Path.read(getCache()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化
|
||||||
|
*/
|
||||||
|
public void init() {
|
||||||
|
String user = cache.getUser().getCookie();
|
||||||
|
if (StringUtils.isNoneBlank(user)) {
|
||||||
|
JsonObject jsonObject = Json.safeObject(user);
|
||||||
|
String username = jsonObject.get("username").getAsString();
|
||||||
|
String password = jsonObject.get("password").getAsString();
|
||||||
|
if (StringUtils.isBlank(username) || StringUtils.isBlank(password)) {
|
||||||
|
this.startFlow();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.loginWithPassword(username, password);
|
||||||
|
} else {
|
||||||
|
this.startFlow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void cleanCookie() {
|
||||||
|
|
||||||
|
cache.setTianyiUser(new User(""));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, String> getHeader(String url) {
|
||||||
|
Map<String, String> headers = new HashMap<>();
|
||||||
|
headers.put("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36");
|
||||||
|
headers.put("Cookie", cookieJar.loadForRequest(url));
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void refreshCookie() throws IOException {
|
||||||
|
|
||||||
|
String url = "https://cloud.189.cn/api/portal/loginUrl.action?redirectURL=https%3A%2F%2Fcloud.189.cn%2Fweb%2Fredirect.html&defaultSaveName=3&defaultSaveNameCheck=uncheck&browserId=16322f24d9405fb83331c3f6ce971b53";
|
||||||
|
String index = OkHttp.getLocation(url, getHeader(url));
|
||||||
|
SpiderDebug.log("unifyAccountLogin:" + index);
|
||||||
|
|
||||||
|
Map<String, List<String>> resHeaderMap = OkHttp.getLocationHeader(index, getHeader(index));
|
||||||
|
saveCookie(resHeaderMap.get("Set-Cookie"), index);
|
||||||
|
indexUrl = resHeaderMap.get("Location").get(0);
|
||||||
|
SpiderDebug.log("callbackUnify: " + indexUrl);
|
||||||
|
|
||||||
|
Map<String, List<String>> callbackUnify = OkHttp.getLocationHeader(indexUrl, getHeader(indexUrl));
|
||||||
|
saveCookie(callbackUnify.get("Set-Cookie"), indexUrl);
|
||||||
|
SpiderDebug.log("refreshCookie header:" + Json.toJson(callbackUnify));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 保存cookie
|
||||||
|
*
|
||||||
|
* @param cookie
|
||||||
|
* @param url
|
||||||
|
*/
|
||||||
|
private void saveCookie(List<String> cookie, String url) {
|
||||||
|
if (cookie != null && cookie.size() > 0) {
|
||||||
|
cookieJar.saveFromResponse(url, cookie);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] startScan() throws Exception {
|
||||||
|
|
||||||
|
|
||||||
|
SpiderDebug.log("index ori: " + "https://cloud.189.cn/api/portal/loginUrl.action?redirectURL=https%3A%2F%2Fcloud.189.cn%2Fweb%2Fredirect.html&defaultSaveName=3&defaultSaveNameCheck=uncheck&browserId=dff95dced0b03d9d972d920f03ddd05e");
|
||||||
|
String index = OkHttp.getLocation("https://cloud.189.cn/api/portal/loginUrl.action?redirectURL=https://cloud.189.cn/web/redirect.html&defaultSaveName=3&defaultSaveNameCheck=uncheck&browserId=8d38da4344fba4699d13d6e6854319d7", Map.of("Cookie", ""));
|
||||||
|
SpiderDebug.log("index red: " + index);
|
||||||
|
Map<String, List<String>> resHeaderMap = OkHttp.getLocationHeader(index, getHeader(index));
|
||||||
|
|
||||||
|
saveCookie(resHeaderMap.get("Set-Cookie"), index);
|
||||||
|
|
||||||
|
indexUrl = resHeaderMap.get("Location").get(0);
|
||||||
|
SpiderDebug.log("indexUrl red: " + indexUrl);
|
||||||
|
|
||||||
|
HttpUrl httpParams = HttpUrl.parse(indexUrl);
|
||||||
|
reqId = httpParams.queryParameter("reqId");
|
||||||
|
lt = httpParams.queryParameter("lt");
|
||||||
|
|
||||||
|
Result result = appConf();
|
||||||
|
|
||||||
|
// Step 1: Get UUID
|
||||||
|
JsonObject uuidInfo = getUUID();
|
||||||
|
String uuid = uuidInfo.get("uuid").getAsString();
|
||||||
|
String encryuuid = uuidInfo.get("encryuuid").getAsString();
|
||||||
|
String encodeuuid = uuidInfo.get("encodeuuid").getAsString();
|
||||||
|
|
||||||
|
// Step 2: Get QR Code
|
||||||
|
byte[] byteStr = downloadQRCode(encodeuuid, reqId);
|
||||||
|
|
||||||
|
Init.run(() -> showQRCode(byteStr));
|
||||||
|
// Step 3: Check login status
|
||||||
|
// return
|
||||||
|
Init.execute(() -> startService(uuid, encryuuid, reqId, lt, result.paramId, result.returnUrl));
|
||||||
|
/*Map<String, Object> result = new HashMap<>();
|
||||||
|
result.put("qrcode", "data:image/png;base64," + qrCode);
|
||||||
|
result.put("status", "NEW");*/
|
||||||
|
return byteStr;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void loginWithPassword(String uname, String passwd) {
|
||||||
|
try {
|
||||||
|
// Step 1: 获取加密配置
|
||||||
|
JsonObject encryptConf = getEncryptConf();
|
||||||
|
String pubKey = encryptConf.getAsJsonObject("data").get("pubKey").getAsString();
|
||||||
|
|
||||||
|
// Step 2: 获取登录参数
|
||||||
|
PasswordLoginParams params = getLoginParams();
|
||||||
|
|
||||||
|
// Step 3: 准备请求头
|
||||||
|
Map<String, String> headers = buildLoginHeaders(params.lt, params.reqId);
|
||||||
|
|
||||||
|
// Step 4: 获取应用配置
|
||||||
|
AppConfig config = getAppConfig(headers);
|
||||||
|
|
||||||
|
// Step 5: 加密凭证
|
||||||
|
EncryptedCredentials credentials = encryptCredentials(uname, passwd, pubKey);
|
||||||
|
|
||||||
|
// Step 6: 提交登录
|
||||||
|
LoginResult loginResult = submitLogin(headers, config, credentials);
|
||||||
|
|
||||||
|
// Step 7: 处理登录结果
|
||||||
|
processLoginResult(loginResult);
|
||||||
|
|
||||||
|
//保存的账号密码
|
||||||
|
JsonObject jsonObject = new JsonObject();
|
||||||
|
jsonObject.addProperty("username", uname);
|
||||||
|
jsonObject.addProperty("password", passwd);
|
||||||
|
cache.setTianyiUser(new User(Json.toJson(jsonObject)));
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
SpiderDebug.log("登录失败: " + e.getMessage());
|
||||||
|
Notify.show("天翼登录失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 辅助方法实现
|
||||||
|
private JsonObject getEncryptConf() throws Exception {
|
||||||
|
String url = API_URL + "/api/logbox/config/encryptConf.do?appId=cloud";
|
||||||
|
OkResult result = OkHttp.post(url, new HashMap<>(), getHeader(url));
|
||||||
|
return Json.safeObject(result.getBody());
|
||||||
|
}
|
||||||
|
|
||||||
|
private PasswordLoginParams getLoginParams() throws Exception {
|
||||||
|
String url = "https://cloud.189.cn/api/portal/loginUrl.action?redirectURL=https://cloud.189.cn/web/redirect.html?returnURL=/main.action";
|
||||||
|
Map<String, List<String>> resHeaderMap = OkHttp.getLocationHeader(url, getHeader(url));
|
||||||
|
|
||||||
|
|
||||||
|
String redUrl = resHeaderMap.get("Location").get(0);
|
||||||
|
resHeaderMap = OkHttp.getLocationHeader(redUrl, getHeader(redUrl));
|
||||||
|
HttpUrl httpUrl = HttpUrl.parse(resHeaderMap.get("Location").get(0));
|
||||||
|
return new PasswordLoginParams(httpUrl.queryParameter("reqId"), httpUrl.queryParameter("lt"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, String> buildLoginHeaders(String lt, String reqId) {
|
||||||
|
Map<String, String> headers = new HashMap<>(getHeader(API_URL));
|
||||||
|
headers.put("Content-Type", "application/x-www-form-urlencoded");
|
||||||
|
headers.put("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:74.0) Gecko/20100101 Firefox/76.0");
|
||||||
|
headers.put("Referer", "https://open.e.189.cn/");
|
||||||
|
headers.put("Lt", lt);
|
||||||
|
headers.put("Reqid", reqId);
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
private AppConfig getAppConfig(Map<String, String> headers) throws Exception {
|
||||||
|
Map<String, String> data = new HashMap<>();
|
||||||
|
data.put("version", "2.0");
|
||||||
|
data.put("appKey", "cloud");
|
||||||
|
|
||||||
|
OkResult result = OkHttp.post(API_URL + "/api/logbox/oauth2/appConf.do", data, headers);
|
||||||
|
JsonObject dataObj = Json.safeObject(result.getBody()).getAsJsonObject("data");
|
||||||
|
return new AppConfig(dataObj.get("returnUrl").getAsString(), dataObj.get("paramId").getAsString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private EncryptedCredentials encryptCredentials(String uname, String passwd, String pubKey) throws Exception {
|
||||||
|
SpiderDebug.log("pubKey: " + pubKey);
|
||||||
|
PublicKey publicKey = parsePublicKey(pubKey);
|
||||||
|
return new EncryptedCredentials(encryptRSA(uname, publicKey), encryptRSA(passwd, publicKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
private LoginResult submitLogin(Map<String, String> headers, AppConfig config, EncryptedCredentials credentials) throws Exception {
|
||||||
|
Map<String, String> data = new HashMap<>();
|
||||||
|
data.put("appKey", "cloud");
|
||||||
|
data.put("version", "2.0");
|
||||||
|
data.put("accountType", "02");
|
||||||
|
//data.put("mailSuffix", "@189.cn");
|
||||||
|
data.put("validateCode", "");
|
||||||
|
data.put("returnUrl", config.returnUrl);
|
||||||
|
data.put("paramId", config.paramId);
|
||||||
|
data.put("captchaToken", "");
|
||||||
|
data.put("dynamicCheck", "FALSE");
|
||||||
|
data.put("clientType", "1");
|
||||||
|
data.put("cb_SaveName", "3");
|
||||||
|
data.put("isOauth2", "false");
|
||||||
|
data.put("userName", "{NRP}" + credentials.encryptedUname);
|
||||||
|
data.put("password", "{NRP}" + credentials.encryptedPasswd);
|
||||||
|
|
||||||
|
OkResult result = OkHttp.post(API_URL + "/api/logbox/oauth2/loginSubmit.do", data, headers);
|
||||||
|
return new LoginResult(Json.safeObject(result.getBody()).get("toUrl").getAsString(), result.getResp().get("Set-Cookie"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processLoginResult(LoginResult result) throws Exception {
|
||||||
|
saveCookie(result.cookies, API_URL + "/api/logbox/oauth2/loginSubmit.do");
|
||||||
|
|
||||||
|
// 处理重定向
|
||||||
|
Map<String, List<String>> okResult = OkHttp.getLocationHeader(result.toUrl, getHeader(result.toUrl));
|
||||||
|
saveCookie(okResult.get("Set-Cookie"), result.toUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 辅助类
|
||||||
|
private static class PasswordLoginParams {
|
||||||
|
final String reqId;
|
||||||
|
final String lt;
|
||||||
|
|
||||||
|
PasswordLoginParams(String reqId, String lt) {
|
||||||
|
this.reqId = reqId;
|
||||||
|
this.lt = lt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class AppConfig {
|
||||||
|
final String returnUrl;
|
||||||
|
final String paramId;
|
||||||
|
|
||||||
|
AppConfig(String returnUrl, String paramId) {
|
||||||
|
this.returnUrl = returnUrl;
|
||||||
|
this.paramId = paramId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class EncryptedCredentials {
|
||||||
|
final String encryptedUname;
|
||||||
|
final String encryptedPasswd;
|
||||||
|
|
||||||
|
EncryptedCredentials(String encryptedUname, String encryptedPasswd) {
|
||||||
|
this.encryptedUname = encryptedUname;
|
||||||
|
this.encryptedPasswd = encryptedPasswd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class LoginResult {
|
||||||
|
final String toUrl;
|
||||||
|
final List<String> cookies;
|
||||||
|
|
||||||
|
LoginResult(String toUrl, List<String> cookies) {
|
||||||
|
this.toUrl = toUrl;
|
||||||
|
this.cookies = cookies;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private PublicKey parsePublicKey(String pubKey) throws Exception {
|
||||||
|
|
||||||
|
byte[] decoded = android.util.Base64.decode(pubKey, Base64.NO_WRAP);
|
||||||
|
X509EncodedKeySpec spec = new X509EncodedKeySpec(decoded);
|
||||||
|
return KeyFactory.getInstance("RSA").generatePublic(spec);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private String encryptRSA(String data, PublicKey publicKey) throws Exception {
|
||||||
|
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
|
||||||
|
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
|
||||||
|
byte[] encrypted = cipher.doFinal(data.getBytes(Charset.defaultCharset()));
|
||||||
|
return bytesToHex(encrypted);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String bytesToHex(byte[] bytes) {
|
||||||
|
StringBuilder hex = new StringBuilder();
|
||||||
|
for (byte b : bytes) {
|
||||||
|
hex.append(String.format("%02x", b));
|
||||||
|
}
|
||||||
|
return hex.toString().toUpperCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String api(String url, Map<String, String> params, Map<String, String> headers, Integer retry, String method) throws InterruptedException {
|
||||||
|
|
||||||
|
|
||||||
|
int leftRetry = retry != null ? retry : 3;
|
||||||
|
|
||||||
|
OkResult okResult;
|
||||||
|
if ("GET".equals(method)) {
|
||||||
|
okResult = OkHttp.get(this.API_URL + url, params, headers);
|
||||||
|
} else {
|
||||||
|
okResult = OkHttp.post(this.API_URL + url, params, headers);
|
||||||
|
}
|
||||||
|
if (okResult.getResp().get("Set-Cookie") != null) {
|
||||||
|
saveCookie(okResult.getResp().get("Set-Cookie"), this.API_URL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (okResult.getCode() != 200 && leftRetry > 0) {
|
||||||
|
SpiderDebug.log("请求" + url + " failed;");
|
||||||
|
Thread.sleep(1000);
|
||||||
|
return api(url, params, headers, leftRetry - 1, method);
|
||||||
|
}
|
||||||
|
SpiderDebug.log("请求" + url + " 成功;" + "返回结果:" + okResult.getBody());
|
||||||
|
return okResult.getBody();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取appConf
|
||||||
|
*
|
||||||
|
* @param
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
|
||||||
|
private @NotNull Result appConf() throws Exception {
|
||||||
|
Map<String, String> tHeaders = getHeader(API_URL);
|
||||||
|
tHeaders.put("Content-Type", "application/x-www-form-urlencoded");
|
||||||
|
tHeaders.put("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:74.0) Gecko/20100101 Firefox/76.0");
|
||||||
|
tHeaders.put("Referer", indexUrl);
|
||||||
|
tHeaders.put("origin", API_URL);
|
||||||
|
tHeaders.put("Lt", lt);
|
||||||
|
tHeaders.put("Reqid", reqId);
|
||||||
|
|
||||||
|
Map<String, String> param = new HashMap<>();
|
||||||
|
|
||||||
|
param.put("version", "2.0");
|
||||||
|
param.put("appKey", "cloud");
|
||||||
|
String paramId;
|
||||||
|
String returnUrl;
|
||||||
|
String body = api("/api/logbox/oauth2/appConf.do", param, tHeaders, 3, "POST");
|
||||||
|
|
||||||
|
paramId = Json.safeObject(body).get("data").getAsJsonObject().get("paramId").getAsString();
|
||||||
|
returnUrl = Json.safeObject(body).get("data").getAsJsonObject().get("returnUrl").getAsString();
|
||||||
|
|
||||||
|
SpiderDebug.log("paramId: " + paramId);
|
||||||
|
SpiderDebug.log("returnUrl: " + returnUrl);
|
||||||
|
return new Result(paramId, returnUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCookie(JsonObject obj) {
|
||||||
|
cookieJar.setGlobalCookie(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Result {
|
||||||
|
public final String paramId;
|
||||||
|
public final String returnUrl;
|
||||||
|
|
||||||
|
public Result(String paramId, String returnUrl) {
|
||||||
|
this.paramId = paramId;
|
||||||
|
this.returnUrl = returnUrl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public JsonObject getUUID() throws InterruptedException {
|
||||||
|
Map<String, String> params = new HashMap<>();
|
||||||
|
params.put("appId", "cloud");
|
||||||
|
Map<String, String> headers = new HashMap<>();
|
||||||
|
headers.put("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36");
|
||||||
|
headers.put("lt", lt);
|
||||||
|
headers.put("reqId", reqId);
|
||||||
|
headers.put("referer", indexUrl);
|
||||||
|
|
||||||
|
|
||||||
|
String body = api("/api/logbox/oauth2/getUUID.do", params, headers, 3, "POST");
|
||||||
|
return Json.safeObject(body);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] downloadQRCode(String uuid, String reqId) throws IOException {
|
||||||
|
|
||||||
|
|
||||||
|
Map<String, String> headers = new HashMap<>();
|
||||||
|
headers.put("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36");
|
||||||
|
|
||||||
|
headers.put("referer", indexUrl);
|
||||||
|
|
||||||
|
// OkResult okResult = OkHttp.get("https://open.e.189.cn/api/logbox/oauth2/image.do", params, headers);
|
||||||
|
//.addQueryParameter("uuid", uuid).addQueryParameter("REQID", reqId)
|
||||||
|
HttpUrl url = HttpUrl.parse(API_URL + "/api/logbox/oauth2/image.do?uuid=" + uuid + "&REQID=" + reqId).newBuilder().build();
|
||||||
|
|
||||||
|
Request request = new Request.Builder().url(url).headers(Headers.of(headers)).build();
|
||||||
|
Response response = OkHttp.newCall(request);
|
||||||
|
if (response.code() == 200) {
|
||||||
|
return response.body().bytes();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private Map<String, Object> checkLoginStatus(String uuid, String encryuuid, String reqId, String lt, String paramId, String returnUrl) throws Exception {
|
||||||
|
Map<String, String> params = new HashMap<>();
|
||||||
|
params.put("appId", "cloud");
|
||||||
|
params.put("encryuuid", encryuuid);
|
||||||
|
params.put("uuid", uuid);
|
||||||
|
params.put("date", DateFormatUtils.format(new Date(), "yyyy-MM-ddHH:mm:ss") + new Random().nextInt(24));
|
||||||
|
params.put("returnUrl", URLEncoder.encode(returnUrl, "UTF-8"));
|
||||||
|
params.put("clientType", "1");
|
||||||
|
params.put("timeStamp", (System.currentTimeMillis() / 1000 + 1) + "000");
|
||||||
|
params.put("cb_SaveName", "0");
|
||||||
|
params.put("isOauth2", "false");
|
||||||
|
params.put("state", "");
|
||||||
|
params.put("paramId", paramId);
|
||||||
|
Map<String, String> headers = new HashMap<>();
|
||||||
|
headers.put("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36");
|
||||||
|
headers.put("referer", indexUrl);
|
||||||
|
headers.put("Reqid", reqId);
|
||||||
|
|
||||||
|
String body = api("/api/logbox/oauth2/qrcodeLoginState.do", params, headers, 3, "POST");
|
||||||
|
// OkResult okResult = OkHttp.post(API_URL + "/api/logbox/oauth2/qrcodeLoginState.do", params, headers);
|
||||||
|
SpiderDebug.log("qrcodeLoginState result------" + body);
|
||||||
|
|
||||||
|
JsonObject obj = Json.safeObject(body).getAsJsonObject();
|
||||||
|
if (Objects.nonNull(obj.get("status")) && obj.get("status").getAsInt() == 0) {
|
||||||
|
|
||||||
|
SpiderDebug.log("扫码成功------" + obj.get("redirectUrl").getAsString());
|
||||||
|
String redirectUrl = obj.get("redirectUrl").getAsString();
|
||||||
|
|
||||||
|
|
||||||
|
fetchUserInfo(redirectUrl);
|
||||||
|
|
||||||
|
|
||||||
|
} else {
|
||||||
|
SpiderDebug.log("扫码失败------" + body);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fetchUserInfo(String redirectUrl) throws IOException {
|
||||||
|
|
||||||
|
|
||||||
|
Map<String, List<String>> okResult = OkHttp.getLocationHeader(redirectUrl, getHeader(redirectUrl));
|
||||||
|
saveCookie(okResult.get("Set-Cookie"), redirectUrl);
|
||||||
|
SpiderDebug.log("扫码返回数据:" + Json.toJson(okResult));
|
||||||
|
if (okResult.containsKey("Set-Cookie")) {
|
||||||
|
|
||||||
|
//停止检验线程,关闭弹窗
|
||||||
|
stopService();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* if (okResult.getCode() == 200) {
|
||||||
|
okResult.getBody();
|
||||||
|
}*/
|
||||||
|
return;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 显示qrcode
|
||||||
|
*
|
||||||
|
* @param bytes
|
||||||
|
*/
|
||||||
|
public void showQRCode(byte[] bytes) {
|
||||||
|
try {
|
||||||
|
int size = ResUtil.dp2px(240);
|
||||||
|
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(size, size);
|
||||||
|
ImageView image = new ImageView(Init.context());
|
||||||
|
image.setScaleType(ImageView.ScaleType.CENTER_CROP);
|
||||||
|
image.setImageBitmap(QRCode.Bytes2Bimap(bytes));
|
||||||
|
FrameLayout frame = new FrameLayout(Init.context());
|
||||||
|
params.gravity = Gravity.CENTER;
|
||||||
|
frame.addView(image, params);
|
||||||
|
dialog = new AlertDialog.Builder(Init.getActivity()).setView(frame).setOnCancelListener(this::dismiss).setOnDismissListener(this::dismiss).show();
|
||||||
|
dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
|
||||||
|
Notify.show("请使用天翼网盘App扫描二维码");
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void startFlow() {
|
||||||
|
Init.run(this::showInput);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void showInput() {
|
||||||
|
try {
|
||||||
|
int margin = ResUtil.dp2px(16);
|
||||||
|
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
|
LinearLayout frame = new LinearLayout(Init.context());
|
||||||
|
frame.setOrientation(LinearLayout.VERTICAL);
|
||||||
|
// frame.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||||
|
// params.setMargins(margin, margin, margin, margin);
|
||||||
|
EditText username = new EditText(Init.context());
|
||||||
|
username.setHint("请输入天翼用户名");
|
||||||
|
EditText password = new EditText(Init.context());
|
||||||
|
password.setHint("请输入天翼密码");
|
||||||
|
frame.addView(username, params);
|
||||||
|
frame.addView(password, params);
|
||||||
|
dialog = new AlertDialog.Builder(Init.getActivity()).setTitle("请输入天意用户名和密码").setView(frame).setNegativeButton(android.R.string.cancel, null).setPositiveButton(android.R.string.ok, (dialog, which) -> onPositive(username.getText().toString(), password.getText().toString())).show();
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void onPositive(String username, String password) {
|
||||||
|
dismiss();
|
||||||
|
Init.execute(() -> {
|
||||||
|
loginWithPassword(username, password);
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void dismiss() {
|
||||||
|
try {
|
||||||
|
if (dialog != null) dialog.dismiss();
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void dismiss(DialogInterface dialog) {
|
||||||
|
stopService();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void stopService() {
|
||||||
|
if (service != null) service.shutdownNow();
|
||||||
|
Init.run(this::dismiss);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void startService(String uuid, String encryuuid, String reqId, String lt, String paramId, String returnUrl) {
|
||||||
|
SpiderDebug.log("----start checkLoginStatus service");
|
||||||
|
|
||||||
|
service = Executors.newScheduledThreadPool(1);
|
||||||
|
|
||||||
|
service.scheduleWithFixedDelay(() -> {
|
||||||
|
SpiderDebug.log("----checkLoginStatus ing....");
|
||||||
|
try {
|
||||||
|
checkLoginStatus(uuid, encryuuid, reqId, lt, paramId, returnUrl);
|
||||||
|
} catch (Exception e) {
|
||||||
|
SpiderDebug.log("----checkLoginStatus error" + e.getMessage());
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}, 1, 3, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,413 @@
|
||||||
|
package com.github.catvod.api;
|
||||||
|
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import com.github.catvod.bean.Result;
|
||||||
|
import com.github.catvod.bean.Vod;
|
||||||
|
import com.github.catvod.bean.tianyi.Item;
|
||||||
|
import com.github.catvod.bean.tianyi.ShareData;
|
||||||
|
import com.github.catvod.crawler.SpiderDebug;
|
||||||
|
import com.github.catvod.net.OkHttp;
|
||||||
|
import com.github.catvod.net.OkResult;
|
||||||
|
import com.github.catvod.spider.Init;
|
||||||
|
import com.github.catvod.utils.*;
|
||||||
|
import com.google.gson.JsonArray;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
public class TianyiApi {
|
||||||
|
private String apiUrl = "https://cloud.189.cn/api/open/share/";
|
||||||
|
public static final String URL_CONTAIN = "cloud.189.cn/";
|
||||||
|
|
||||||
|
|
||||||
|
private Map<String, JsonObject> shareTokenCache = new HashMap<>();
|
||||||
|
|
||||||
|
|
||||||
|
private ScheduledExecutorService service;
|
||||||
|
private String sessionKey = "";
|
||||||
|
private TianYiHandler tianYiHandler;
|
||||||
|
private SimpleCookieJar cookieJar;
|
||||||
|
|
||||||
|
|
||||||
|
public String[] getPlayFormatList() {
|
||||||
|
return new String[]{"天意"};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Loader {
|
||||||
|
static volatile TianyiApi INSTANCE = new TianyiApi();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TianyiApi get() {
|
||||||
|
return TianyiApi.Loader.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCookie(String token) throws Exception {
|
||||||
|
if (StringUtils.isNoneBlank(token)) {
|
||||||
|
JsonObject obj = Json.safeObject(token);
|
||||||
|
//初始化CookieJar
|
||||||
|
if (Objects.nonNull(obj)) {
|
||||||
|
tianYiHandler.setCookie(obj);
|
||||||
|
tianYiHandler.loginWithPassword(obj.get("username").getAsString(), obj.get("password").getAsString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!isCookieValid()) {
|
||||||
|
SpiderDebug.log("CookieJar不合法,请重新登录");
|
||||||
|
// tianYiHandler.startScan();
|
||||||
|
}
|
||||||
|
getUserSizeInfo();
|
||||||
|
this.sessionKey = getUserBriefInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断cookie是否为空,或者SSon为空,那就需要重新登陆
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private boolean isCookieValid() {
|
||||||
|
if (cookieJar.getCookieStore().size() == 0) {
|
||||||
|
SpiderDebug.log("CookieJar为空");
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
for (String key : cookieJar.getCookieStore().keySet()) {
|
||||||
|
Map<String, String> cookieMap = cookieJar.getCookieStore().get(key);
|
||||||
|
for (String k : cookieMap.keySet()) {
|
||||||
|
String cookieobj = cookieMap.get(k);
|
||||||
|
if (k.equals("SSON") && StringUtils.isNoneBlank(cookieobj)) {
|
||||||
|
SpiderDebug.log("SSON 不为空");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SpiderDebug.log("CookieJar 不合法,重新登录");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, String> getHeaders() {
|
||||||
|
Map<String, String> headers = new HashMap<>();
|
||||||
|
headers.put("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) quark-cloud-drive/2.5.20 Chrome/100.0.4896.160 Electron/18.3.5.4-b478491100 Safari/537.36 Channel/pckk_other_ch");
|
||||||
|
|
||||||
|
headers.put("Content-Type", "application/x-www-form-urlencoded");
|
||||||
|
headers.put("accept", "application/json;charset=UTF-8");
|
||||||
|
headers.put("cookie", cookieJar.loadForRequest("https://cloud.189.cn/api/portal/getNewVlcVideoPlayUrl.action"));
|
||||||
|
|
||||||
|
if (StringUtils.isNotBlank(sessionKey)) {
|
||||||
|
headers.put("sessionKey", sessionKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private TianyiApi() {
|
||||||
|
Init.checkPermission();
|
||||||
|
|
||||||
|
tianYiHandler = TianYiHandler.get();
|
||||||
|
tianYiHandler.init();
|
||||||
|
cookieJar = tianYiHandler.getCookieJar();
|
||||||
|
}
|
||||||
|
|
||||||
|
public File getCache() {
|
||||||
|
return Path.tv("tianyi");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vod getVod(ShareData shareData) throws Exception {
|
||||||
|
getShareToken(shareData);
|
||||||
|
List<Item> files = new ArrayList<>();
|
||||||
|
List<Item> subs = new ArrayList<>();
|
||||||
|
try {
|
||||||
|
JsonArray listData = listFile(1, shareData, files, subs, shareData.getShareId(), shareData.getFolderId(), 1);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
SpiderDebug.log("资源已取消:" + e.getMessage());
|
||||||
|
Notify.show("资源已取消");
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
List<String> playFromtmp = new ArrayList<>();
|
||||||
|
playFromtmp.add("天意");
|
||||||
|
|
||||||
|
|
||||||
|
List<String> playUrl = new ArrayList<>();
|
||||||
|
|
||||||
|
if (files.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int index = 0; index < playFromtmp.size(); index++) {
|
||||||
|
List<String> vodItems = new ArrayList<>();
|
||||||
|
for (Item video_item : files) {
|
||||||
|
vodItems.add(video_item.getEpisodeUrl("电影"));// + findSubs(video_item.getName(), subs));
|
||||||
|
}
|
||||||
|
playUrl.add(TextUtils.join("#", vodItems));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Vod vod = new Vod();
|
||||||
|
vod.setVodId("");
|
||||||
|
vod.setVodContent("");
|
||||||
|
vod.setVodPic("");
|
||||||
|
vod.setVodName("");
|
||||||
|
vod.setVodPlayUrl(TextUtils.join("$$$", playUrl));
|
||||||
|
vod.setVodPlayFrom(TextUtils.join("$$$", playFromtmp));
|
||||||
|
vod.setTypeName("天意云盘");
|
||||||
|
return vod;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String playerContent(String[] split, String flag) throws Exception {
|
||||||
|
|
||||||
|
String fileId = split[0], shareId = split[1];
|
||||||
|
String playUrl = "";
|
||||||
|
|
||||||
|
playUrl = this.getDownload(shareId, fileId);
|
||||||
|
Map<String, String> header = getHeaders();
|
||||||
|
header.remove("Host");
|
||||||
|
header.remove("Content-Type");
|
||||||
|
|
||||||
|
|
||||||
|
header.put("Cookie", cookieJar.loadForRequest("https://cloud.189.cn/api/portal/getNewVlcVideoPlayUrl.action"));
|
||||||
|
return Result.get().url(ProxyServer.INSTANCE.buildProxyUrl(playUrl, header)).octet().header(header).string();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param url
|
||||||
|
* @param params get 参数
|
||||||
|
* @param data post json
|
||||||
|
* @param retry
|
||||||
|
* @param method
|
||||||
|
* @return
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
private String api(String url, Map<String, String> params, Map<String, Object> data, Integer retry, String method) throws Exception {
|
||||||
|
|
||||||
|
|
||||||
|
int leftRetry = retry != null ? retry : 3;
|
||||||
|
|
||||||
|
OkResult okResult;
|
||||||
|
if ("GET".equals(method)) {
|
||||||
|
okResult = OkHttp.get(this.apiUrl + url, params, getHeaders());
|
||||||
|
} else {
|
||||||
|
okResult = OkHttp.post(this.apiUrl + url, Json.toJson(data), getHeaders());
|
||||||
|
}
|
||||||
|
/* if (okResult.getResp().get("Set-Cookie") != null) {
|
||||||
|
Matcher matcher = Pattern.compile("__puus=([^;]+)").matcher(StringUtils.join(okResult.getResp().get("Set-Cookie"), ";;;"));
|
||||||
|
if (matcher.find()) {
|
||||||
|
Matcher cookieMatcher = Pattern.compile("__puus=([^;]+)").matcher(this.cookie);
|
||||||
|
if (cookieMatcher.find() && !cookieMatcher.group(1).equals(matcher.group(1))) {
|
||||||
|
this.cookie = this.cookie.replaceAll("__puus=[^;]+", "__puus=" + matcher.group(1));
|
||||||
|
} else {
|
||||||
|
this.cookie = this.cookie + ";__puus=" + matcher.group(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
if (okResult.getCode() != 200 && leftRetry > 0) {
|
||||||
|
SpiderDebug.log("api error code:" + okResult.getCode());
|
||||||
|
Thread.sleep(1000);
|
||||||
|
return api(url, params, data, leftRetry - 1, method);
|
||||||
|
}
|
||||||
|
return okResult.getBody();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public ShareData getShareData(String url, String accessCode) {
|
||||||
|
// 从整个URL中直接提取访问码,无论格式如何
|
||||||
|
Matcher accessMatcher = Pattern.compile("访问码[::]([a-zA-Z0-9]+)").matcher(url);
|
||||||
|
if (accessMatcher.find()) {
|
||||||
|
accessCode = accessMatcher.group(1);
|
||||||
|
} else if (accessCode == null || accessCode.isEmpty()) {
|
||||||
|
accessCode = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
String shareCode = null;
|
||||||
|
// 第一种匹配规则:兼容http和https
|
||||||
|
Matcher matcher = Pattern.compile("https?:\\/\\/cloud\\.189\\.cn\\/web\\/share\\?code=([^&\\s]+)").matcher(url);
|
||||||
|
if (matcher.find() && matcher.group(1) != null) {
|
||||||
|
shareCode = matcher.group(1);
|
||||||
|
} else {
|
||||||
|
// 第二种匹配规则:直接匹配 cloud.189.cn/t/ 格式,兼容http和https,匹配到空格前
|
||||||
|
Matcher fallbackMatcher = Pattern.compile("https?:\\/\\/cloud\\.189\\.cn\\/t/([^\\s]+)").matcher(url);
|
||||||
|
if (fallbackMatcher.find()) {
|
||||||
|
shareCode = fallbackMatcher.group(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ShareData shareData = new ShareData(shareCode, "0");
|
||||||
|
shareData.setSharePwd(accessCode);
|
||||||
|
return shareData;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private String getUserBriefInfo() throws Exception {
|
||||||
|
OkResult result = OkHttp.get("https://cloud.189.cn/api/portal/v2/getUserBriefInfo.action", new HashMap<>(), getHeaders());
|
||||||
|
JsonObject obj = Json.safeObject(result.getBody());
|
||||||
|
return obj.get("sessionKey") == null ? "" : obj.get("sessionKey").getAsString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getUserSizeInfo() throws Exception {
|
||||||
|
OkResult result = OkHttp.get("https://cloud.189.cn/api/portal/getUserSizeInfo.action", new HashMap<>(), getHeaders());
|
||||||
|
JsonObject res = Json.safeObject(result.getBody());
|
||||||
|
if (StringUtils.isAllBlank(result.getBody()) || (Objects.nonNull(res.get("errorCode")) && res.get("errorCode").getAsString().equals("InvalidSessionKey"))) {
|
||||||
|
// tianYiHandler.startScan();
|
||||||
|
tianYiHandler.refreshCookie();
|
||||||
|
//tianYiHandler.startScan();
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void getShareToken(ShareData shareData) throws Exception {
|
||||||
|
if (!this.shareTokenCache.containsKey(shareData.getShareId())) {
|
||||||
|
this.shareTokenCache.remove(shareData.getShareId());
|
||||||
|
JsonObject shareToken = Json.safeObject(api("getShareInfoByCodeV2.action?noCache=0.8886566349412803&shareCode=" + shareData.getShareId(), new HashMap<>(), new HashMap<>(), 0, "GET"));
|
||||||
|
/**
|
||||||
|
* {
|
||||||
|
* "res_code" : 0.0,
|
||||||
|
* "res_message" : "成功",
|
||||||
|
* "accessCode" : "",
|
||||||
|
* "creator" : {
|
||||||
|
* "iconURL" : "",
|
||||||
|
* "oper" : false,
|
||||||
|
* "ownerAccount" : "185****1601@189.cn",
|
||||||
|
* "superVip" : 33.0,
|
||||||
|
* "vip" : 0.0
|
||||||
|
* },
|
||||||
|
* "expireTime" : 6.0,
|
||||||
|
* "expireType" : 1.0,
|
||||||
|
* "fileCreateDate" : "2025-03-20 13:49:18",
|
||||||
|
* "fileId" : "12350115314094",
|
||||||
|
* "fileLastOpTime" : "2025-03-20 13:49:19",
|
||||||
|
* "fileName" : "05_如何制作动感影集.mp4等",
|
||||||
|
* "fileSize" : 0.0,
|
||||||
|
* "fileType" : "batchShare",
|
||||||
|
* "isFolder" : true,
|
||||||
|
* "needAccessCode" : 1.0,
|
||||||
|
* "reviewStatus" : 1.0,
|
||||||
|
* "shareDate" : 1.742449758E12,
|
||||||
|
* "shareMode" : 1.0,
|
||||||
|
* "shareType" : 1.0
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
if (Objects.nonNull(shareToken.get("res_code")) && shareToken.get("res_code").getAsInt() == 0) {
|
||||||
|
shareData.setShareId(shareToken.get("shareId").getAsString());
|
||||||
|
shareData.setShareMode(shareToken.get("shareMode").getAsInt());
|
||||||
|
shareData.setFolder(shareToken.get("isFolder").getAsBoolean());
|
||||||
|
shareData.setFileId(shareToken.get("fileId").getAsString());
|
||||||
|
shareData.setFolderId(shareToken.get("fileId").getAsString());
|
||||||
|
|
||||||
|
this.shareTokenCache.put(shareData.getShareId(), shareToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private JsonArray listFile(int shareIndex, ShareData shareData, List<Item> videos, List<Item> subtitles, String shareId, String folderId, Integer page) throws Exception {
|
||||||
|
int prePage = 200;
|
||||||
|
page = page != null ? page : 1;
|
||||||
|
String url = "listShareDir.action?" + "pageNum=" + page + "&pageSize=" + prePage + "&fileId=" + folderId + "&shareDirFileId=" + folderId + "&isFolder=" + shareData.getFolder() + "&shareId=" + shareId + "&shareMode=" + shareData.getShareMode() + "&iconOption=5" + "&orderBy=filename" + "&descending=false" + "&accessCode=" + shareData.getSharePwd();
|
||||||
|
|
||||||
|
JsonObject listData = Json.safeObject(api(url, Collections.emptyMap(), Collections.emptyMap(), 0, "GET"));
|
||||||
|
if (listData.get("res_code").getAsInt() != 0) return new JsonArray();
|
||||||
|
if (listData.get("fileListAO").getAsJsonObject().get("count").getAsInt() == 0 && listData.get("fileListAO").getAsJsonObject().get("fileListSize").getAsInt() == 0)
|
||||||
|
return new JsonArray();
|
||||||
|
|
||||||
|
JsonArray items = listData.get("fileListAO").getAsJsonObject().get("fileList").getAsJsonArray();
|
||||||
|
JsonArray subDir = listData.get("fileListAO").getAsJsonObject().get("folderList").getAsJsonArray();
|
||||||
|
|
||||||
|
for (JsonElement item : items) {
|
||||||
|
if (item.getAsJsonObject().get("mediaType").getAsInt() == 3) {
|
||||||
|
if (item.getAsJsonObject().get("size").getAsLong() < 1024 * 1024 * 5) continue;
|
||||||
|
|
||||||
|
videos.add(Item.objectFrom(item.getAsJsonObject(), shareData.getShareId(), shareIndex));
|
||||||
|
} /*else if ("file".equals(item.get("type")) && this.subtitleExts.contains("." + Util.getExt((String) item.get("file_name")))) {
|
||||||
|
subtitles.add(Item.objectFrom(item, shareData.getShareId(), shareIndex));
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
if (listData.get("fileListAO").getAsJsonObject().get("count").getAsInt() > (items.size() + subDir.size())) {
|
||||||
|
JsonArray nextItems = listFile(shareIndex, shareData, videos, subtitles, shareId, folderId, page + 1);
|
||||||
|
items.addAll(nextItems);
|
||||||
|
}
|
||||||
|
for (JsonElement dir : subDir) {
|
||||||
|
String subfolderId = dir.getAsJsonObject().get("id").getAsString();
|
||||||
|
JsonArray subItems = listFile(shareIndex, shareData, videos, subtitles, shareId, subfolderId, null);
|
||||||
|
items.addAll(subItems);
|
||||||
|
}
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, Object> findBestLCS(Item mainItem, List<Item> targetItems) {
|
||||||
|
List<Map<String, Object>> results = new ArrayList<>();
|
||||||
|
int bestMatchIndex = 0;
|
||||||
|
for (int i = 0; i < targetItems.size(); i++) {
|
||||||
|
Util.LCSResult currentLCS = Util.lcs(mainItem.getName(), targetItems.get(i).getName());
|
||||||
|
Map<String, Object> result = new HashMap<>();
|
||||||
|
result.put("target", targetItems.get(i));
|
||||||
|
result.put("lcs", currentLCS);
|
||||||
|
results.add(result);
|
||||||
|
if (currentLCS.length > results.get(bestMatchIndex).get("lcs").toString().length()) {
|
||||||
|
bestMatchIndex = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Map<String, Object> bestMatch = results.get(bestMatchIndex);
|
||||||
|
Map<String, Object> finalResult = new HashMap<>();
|
||||||
|
finalResult.put("allLCS", results);
|
||||||
|
finalResult.put("bestMatch", bestMatch);
|
||||||
|
finalResult.put("bestMatchIndex", bestMatchIndex);
|
||||||
|
return finalResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void getFilesByShareUrl(int shareIndex, String shareInfo, List<Item> videos, List<Item> subtitles) throws Exception {
|
||||||
|
ShareData shareData = getShareData((String) shareInfo, "");
|
||||||
|
if (shareData == null) return;
|
||||||
|
getShareToken(shareData);
|
||||||
|
if (!this.shareTokenCache.containsKey(shareData.getShareId())) return;
|
||||||
|
listFile(shareIndex, shareData, videos, subtitles, shareData.getShareId(), shareData.getFolderId(), 1);
|
||||||
|
if (!subtitles.isEmpty()) {
|
||||||
|
for (Item video : videos) {
|
||||||
|
Map<String, Object> matchSubtitle = findBestLCS(video, subtitles);
|
||||||
|
if (matchSubtitle.get("bestMatch") != null) {
|
||||||
|
video.setSubtitle((String) ((Map<String, Object>) matchSubtitle.get("bestMatch")).get("target"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private String getDownload(String shareId, String fileId) throws Exception {
|
||||||
|
Map<String, String> headers = getHeaders();
|
||||||
|
//headers.remove("sessionKey");
|
||||||
|
OkResult result = OkHttp.get("https://cloud.189.cn/api/portal/getNewVlcVideoPlayUrl.action?shareId=" + shareId + "&dt=1&fileId=" + fileId + "&type=4&key=noCache", new HashMap<>(), headers);
|
||||||
|
JsonObject res = Json.safeObject(result.getBody());
|
||||||
|
if (Objects.nonNull(res.get("res_code")) && res.get("res_code").getAsInt() == 0) {
|
||||||
|
|
||||||
|
if (res.get("normal") != null) {
|
||||||
|
String normal = res.get("normal").getAsJsonObject().get("url").getAsString();
|
||||||
|
//String downloadUrl = OkHttp.getLocation(normal, headers);
|
||||||
|
SpiderDebug.log("获取天翼下载地址成功:" + normal);
|
||||||
|
return normal;
|
||||||
|
}
|
||||||
|
} else if (res.get("errorCode") != null && res.get("errorCode").getAsString().equals("InvalidSessionKey")) {
|
||||||
|
//刷新cookie
|
||||||
|
SpiderDebug.log("天意cookie 过期,刷新cookie。。。。");
|
||||||
|
tianYiHandler.refreshCookie();
|
||||||
|
//重试下载
|
||||||
|
SpiderDebug.log("重试下载。。。。");
|
||||||
|
getDownload(shareId, fileId);
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,753 @@
|
||||||
|
package com.github.catvod.api;
|
||||||
|
|
||||||
|
import android.app.AlertDialog;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.graphics.drawable.ColorDrawable;
|
||||||
|
import android.os.SystemClock;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.view.Gravity;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.FrameLayout;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import com.github.catvod.bean.Result;
|
||||||
|
import com.github.catvod.bean.Vod;
|
||||||
|
import com.github.catvod.bean.uc.Cache;
|
||||||
|
import com.github.catvod.bean.uc.Item;
|
||||||
|
import com.github.catvod.bean.uc.ShareData;
|
||||||
|
import com.github.catvod.bean.uc.User;
|
||||||
|
import com.github.catvod.crawler.SpiderDebug;
|
||||||
|
import com.github.catvod.net.OkHttp;
|
||||||
|
import com.github.catvod.net.OkResult;
|
||||||
|
import com.github.catvod.spider.Init;
|
||||||
|
import com.github.catvod.spider.Proxy;
|
||||||
|
import com.github.catvod.utils.*;
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import org.apache.commons.lang3.RandomUtils;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
public class UCApi {
|
||||||
|
private String apiUrl = "https://pc-api.uc.cn/1/clouddrive/";
|
||||||
|
private String cookie = "";
|
||||||
|
private String cookieToken = "";
|
||||||
|
private String ckey = "";
|
||||||
|
private Map<String, Map<String, Object>> shareTokenCache = new HashMap<>();
|
||||||
|
private String pr = "pr=UCBrowser&fr=pc&sys=darwin&ve=1.8.6&ut=Nk27FcCv6q1eo6rXz8QHR/nIG6qLA3jh7KdL+agFgcOvww==";
|
||||||
|
private List<String> subtitleExts = Arrays.asList(".srt", ".ass", ".scc", ".stl", ".ttml");
|
||||||
|
private Map<String, String> saveFileIdCaches = new HashMap<>();
|
||||||
|
private String saveDirId = null;
|
||||||
|
private final String saveDirName = "TV";
|
||||||
|
private boolean isVip = false;
|
||||||
|
private final Cache cache;
|
||||||
|
private final Cache tokenCache;
|
||||||
|
private ScheduledExecutorService service;
|
||||||
|
|
||||||
|
|
||||||
|
private AlertDialog dialog;
|
||||||
|
private String serviceTicket;
|
||||||
|
private UCTokenHandler qrCodeHandler;
|
||||||
|
|
||||||
|
private Map<String, String> getHeaders() {
|
||||||
|
Map<String, String> headers = new HashMap<>();
|
||||||
|
headers.put("User-Agent", "Mozilla/5.0 (Linux; Android 8.0.0; SM-G955U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Mobile Safari/537.36");
|
||||||
|
headers.put("Referer", "https://drive.uc.cn");
|
||||||
|
headers.put("Content-Type", "application/json");
|
||||||
|
headers.put("Cookie", cookie);
|
||||||
|
//headers.put("Host", "drive-pc.quark.cn");
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, String> getWebHeaders() {
|
||||||
|
Map<String, String> headers = new HashMap<>();
|
||||||
|
headers.put("User-Agent", "Mozilla/5.0 (Linux; Android 8.0.0; SM-G955U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Mobile Safari/537.36");
|
||||||
|
headers.put("Referer", "https://drive.uc.cn");
|
||||||
|
headers.put("Cookie", cookie);
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* cookieToken = qrCodeHandler.startUC_TOKENScan();
|
||||||
|
SpiderDebug.log("扫码登录获取到的cookieToken: " + cookieToken);*/
|
||||||
|
|
||||||
|
|
||||||
|
private UCApi() {
|
||||||
|
Init.checkPermission();
|
||||||
|
qrCodeHandler = new UCTokenHandler();
|
||||||
|
cache = Cache.objectFrom(Path.read(getCache()));
|
||||||
|
tokenCache = Cache.objectFrom(Path.read(qrCodeHandler.getCache()));
|
||||||
|
|
||||||
|
java.lang.String tokenCacheJson = tokenCache.getUser().getCookie();
|
||||||
|
if (StringUtils.isNoneBlank(tokenCacheJson)) {
|
||||||
|
|
||||||
|
|
||||||
|
//刷新token,并返回
|
||||||
|
this.cookieToken = qrCodeHandler.refreshToken(Json.safeObject(tokenCacheJson).getAsJsonObject().get("refresh_token").getAsString());
|
||||||
|
|
||||||
|
SpiderDebug.log("UC初始化获取到的cookieToken: " + cookieToken);
|
||||||
|
}
|
||||||
|
SpiderDebug.log("UC初始化获取到的cookieToken: " + cookieToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Loader {
|
||||||
|
static volatile UCApi INSTANCE = new UCApi();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UCApi get() {
|
||||||
|
return UCApi.Loader.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
//从配置中获取cookie
|
||||||
|
public void setCookie(String token) throws Exception {
|
||||||
|
if (StringUtils.isNoneBlank(token)) {
|
||||||
|
this.cookie = token;
|
||||||
|
initUserInfo();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化UC信息
|
||||||
|
*/
|
||||||
|
private void initUserInfo() {
|
||||||
|
try {
|
||||||
|
SpiderDebug.log("uc initUserInfo...");
|
||||||
|
|
||||||
|
//extend没有cookie,从缓存中获取
|
||||||
|
if (StringUtils.isAllBlank(cookie)) {
|
||||||
|
SpiderDebug.log("uc cookie from ext is empty...");
|
||||||
|
cookie = cache.getUser().getCookie();
|
||||||
|
}
|
||||||
|
//获取到cookie,初始化uc,并且把cookie缓存一次
|
||||||
|
if (StringUtils.isNoneBlank(cookie) && cookie.contains("__pus")) {
|
||||||
|
SpiderDebug.log(" initUc ...");
|
||||||
|
initUc(this.cookie);
|
||||||
|
cache.setUser(User.objectFrom(this.cookie));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//没有cookie,也没有serviceTicket,抛出异常,提示用户重新登录
|
||||||
|
if (StringUtils.isAllBlank(cookie) && StringUtils.isAllBlank(serviceTicket)) {
|
||||||
|
SpiderDebug.log("uccookie为空");
|
||||||
|
throw new RuntimeException("uccookie为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
String token = serviceTicket;
|
||||||
|
OkResult result = OkHttp.get("https://drive.uc.cn/account/info?st=" + token + "", new HashMap<>(), getWebHeaders());
|
||||||
|
Map json = Json.parseSafe(result.getBody(), Map.class);
|
||||||
|
if (json.get("success").equals(Boolean.TRUE)) {
|
||||||
|
List<String> cookies = result.getResp().get("set-Cookie");
|
||||||
|
List<String> cookieList = new ArrayList<>();
|
||||||
|
for (String cookie : cookies) {
|
||||||
|
cookieList.add(cookie.split(";")[0]);
|
||||||
|
}
|
||||||
|
this.cookie += TextUtils.join(";", cookieList);
|
||||||
|
|
||||||
|
cache.setUser(User.objectFrom(this.cookie));
|
||||||
|
if (cache.getUser().getCookie().isEmpty()) throw new Exception(this.cookie);
|
||||||
|
initUc(this.cookie);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
cache.getUser().clean();
|
||||||
|
e.printStackTrace();
|
||||||
|
stopService();
|
||||||
|
startFlow();
|
||||||
|
} finally {
|
||||||
|
while (cache.getUser().getCookie().isEmpty()) SystemClock.sleep(250);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void initUc(String cookie) throws Exception {
|
||||||
|
this.ckey = Util.MD5(cookie);
|
||||||
|
this.cookie = cookie;
|
||||||
|
this.isVip = getVip();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public File getCache() {
|
||||||
|
return Path.tv("uc");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Vod getVod(ShareData shareData) throws Exception {
|
||||||
|
getShareToken(shareData);
|
||||||
|
List<Item> files = new ArrayList<>();
|
||||||
|
List<Item> subs = new ArrayList<>();
|
||||||
|
List<Map<String, Object>> listData = listFile(1, shareData, files, subs, shareData.getShareId(), shareData.getFolderId(), 1);
|
||||||
|
|
||||||
|
List<String> playFrom = UCApi.get().getPlayFormatList();
|
||||||
|
List<String> playFromtmp = new ArrayList<>();
|
||||||
|
playFromtmp.add("uc原画");
|
||||||
|
/* for (String s : playFrom) {
|
||||||
|
playFromtmp.add("uc" + s);
|
||||||
|
}*/
|
||||||
|
List<String> playUrl = new ArrayList<>();
|
||||||
|
|
||||||
|
if (files.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < files.get(files.size() - 1).getShareIndex(); i++) {
|
||||||
|
for (int index = 0; index < playFromtmp.size(); index++) {
|
||||||
|
List<String> vodItems = new ArrayList<>();
|
||||||
|
for (Item video_item : files) {
|
||||||
|
if (video_item.getShareIndex() == i + 1) {
|
||||||
|
vodItems.add(video_item.getEpisodeUrl("电影"));// + findSubs(video_item.getName(), subs));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
playUrl.add(TextUtils.join("#", vodItems));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Vod vod = new Vod();
|
||||||
|
vod.setVodId("");
|
||||||
|
vod.setVodContent("");
|
||||||
|
vod.setVodPic("");
|
||||||
|
vod.setVodName("");
|
||||||
|
vod.setVodPlayUrl(TextUtils.join("$$$", playUrl));
|
||||||
|
vod.setVodPlayFrom(TextUtils.join("$$$", playFromtmp));
|
||||||
|
vod.setTypeName("uc云盘");
|
||||||
|
return vod;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String playerContent(String[] split, String flag) throws Exception {
|
||||||
|
SpiderDebug.log("flag:" + flag);
|
||||||
|
String fileId = split[0], fileToken = split[1], shareId = split[2], stoken = split[3];
|
||||||
|
String playUrl = "";
|
||||||
|
SpiderDebug.log("origin playUrl:" + playUrl);
|
||||||
|
Map<String, String> header = getHeaders();
|
||||||
|
header.remove("Host");
|
||||||
|
header.remove("Content-Type");
|
||||||
|
if (flag.contains("uc原画")) {
|
||||||
|
playUrl = this.getDownload(shareId, stoken, fileId, fileToken, true);
|
||||||
|
return Result.get().url(playUrl).string();
|
||||||
|
} else {
|
||||||
|
playUrl = this.getLiveTranscoding(shareId, stoken, fileId, fileToken, flag);
|
||||||
|
return Result.get().url(proxyVideoUrl(playUrl, new HashMap<>())).string();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean testVideo(String url) {
|
||||||
|
|
||||||
|
OkResult okResult1 = OkHttp.get(url, new HashMap<>(), Map.of("Range", "bytes=0-0"));
|
||||||
|
return okResult1.getCode() == 206;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private String proxyVideoUrl(String url, Map<String, String> header) {
|
||||||
|
return String.format(Proxy.getUrl() + "?do=uc&type=video&url=%s&header=%s", Util.base64Encode(url.getBytes(Charset.defaultCharset())), Util.base64Encode(Json.toJson(header).getBytes(Charset.defaultCharset())));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object[] proxyVideo(Map<String, String> params) throws Exception {
|
||||||
|
String url = Util.base64Decode(params.get("url"));
|
||||||
|
SpiderDebug.log("proxy url :" + url);
|
||||||
|
SpiderDebug.log("proxy header :" + Util.base64Decode(params.get("header")));
|
||||||
|
Map header = new Gson().fromJson(Util.base64Decode(params.get("header")), Map.class);
|
||||||
|
if (header == null) header = new HashMap<>();
|
||||||
|
List<String> arr = List.of("Range", "Accept", "Accept-Encoding", "Accept-Language", "Cookie", "Origin", "Referer", "Sec-Ch-Ua", "Sec-Ch-Ua-Mobile", "Sec-Ch-Ua-Platform", "Sec-Fetch-Dest", "Sec-Fetch-Mode", "Sec-Fetch-Site", "User-Agent");
|
||||||
|
for (String key : params.keySet()) {
|
||||||
|
for (String s : arr) {
|
||||||
|
if (s.toLowerCase().equals(key.toLowerCase())) {
|
||||||
|
header.put(key, params.get(key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if (Util.getExt(url).contains("m3u8")) {
|
||||||
|
return getM3u8(url, header);
|
||||||
|
}
|
||||||
|
return ProxyVideo.proxy(url, header);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 代理m3u8
|
||||||
|
*
|
||||||
|
* @param url
|
||||||
|
* @param header
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private Object[] getM3u8(String url, Map header) {
|
||||||
|
SpiderDebug.log("m3u8 url :" + url);
|
||||||
|
OkResult result = OkHttp.get(url, new HashMap<>(), header);
|
||||||
|
String[] m3u8Arr = result.getBody().split("\n");
|
||||||
|
List<String> listM3u8 = new ArrayList<>();
|
||||||
|
|
||||||
|
String site = url.substring(0, url.lastIndexOf("/")) + "/";
|
||||||
|
int mediaId = 0;
|
||||||
|
for (String oneLine : m3u8Arr) {
|
||||||
|
String thisOne = oneLine;
|
||||||
|
|
||||||
|
if (oneLine.contains(".ts")) {
|
||||||
|
mediaId++;
|
||||||
|
thisOne = proxyVideoUrl(site + thisOne, header);
|
||||||
|
SpiderDebug.log("m3u8 line " + mediaId + ":" + oneLine);
|
||||||
|
SpiderDebug.log("m3u8 proxyed line " + mediaId + " :" + thisOne);
|
||||||
|
|
||||||
|
}
|
||||||
|
listM3u8.add(thisOne);
|
||||||
|
}
|
||||||
|
String m3u8Str = TextUtils.join("\n", listM3u8);
|
||||||
|
String contentType = result.getResp().get("Content-Type").get(0);
|
||||||
|
|
||||||
|
Map<String, String> respHeaders = new HashMap<>();
|
||||||
|
// respHeaders.put("Access-Control-Allow-Origin","*");
|
||||||
|
// respHeaders.put("Access-Control-Allow-Credentials","true");
|
||||||
|
for (String key : result.getResp().keySet()) {
|
||||||
|
respHeaders.put(key, result.getResp().get(key).get(0));
|
||||||
|
}
|
||||||
|
return new Object[]{result.getCode(), contentType, new ByteArrayInputStream(m3u8Str.getBytes(Charset.defaultCharset())), respHeaders};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param url
|
||||||
|
* @param params get 参数
|
||||||
|
* @param data post json
|
||||||
|
* @param retry
|
||||||
|
* @param method
|
||||||
|
* @return
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
private String api(String url, Map<String, String> params, Map<String, Object> data, Integer retry, String method) throws Exception {
|
||||||
|
|
||||||
|
|
||||||
|
int leftRetry = retry != null ? retry : 3;
|
||||||
|
if (StringUtils.isAllBlank(cookie)) {
|
||||||
|
this.initUserInfo();
|
||||||
|
return api(url, params, data, leftRetry - 1, method);
|
||||||
|
}
|
||||||
|
OkResult okResult;
|
||||||
|
if ("GET".equals(method)) {
|
||||||
|
okResult = OkHttp.get(this.apiUrl + url, params, getHeaders());
|
||||||
|
} else {
|
||||||
|
okResult = OkHttp.post(this.apiUrl + url, Json.toJson(data), getHeaders());
|
||||||
|
}
|
||||||
|
if (okResult.getResp().get("Set-Cookie") != null) {
|
||||||
|
Matcher matcher = Pattern.compile("__puus=([^;]+)").matcher(StringUtils.join(okResult.getResp().get("Set-Cookie"), ";;;"));
|
||||||
|
if (matcher.find()) {
|
||||||
|
Matcher cookieMatcher = Pattern.compile("__puus=([^;]+)").matcher(this.cookie);
|
||||||
|
if (cookieMatcher.find() && !cookieMatcher.group(1).equals(matcher.group(1))) {
|
||||||
|
this.cookie = this.cookie.replaceAll("__puus=[^;]+", "__puus=" + matcher.group(1));
|
||||||
|
} else {
|
||||||
|
this.cookie = this.cookie + ";__puus=" + matcher.group(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (okResult.getCode() != 200 && leftRetry > 0) {
|
||||||
|
SpiderDebug.log("api error code:" + okResult.getCode());
|
||||||
|
Thread.sleep(1000);
|
||||||
|
return api(url, params, data, leftRetry - 1, method);
|
||||||
|
}
|
||||||
|
return okResult.getBody();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取二维码登录的令牌
|
||||||
|
*
|
||||||
|
* @return 返回包含二维码登录令牌的字符串
|
||||||
|
*/
|
||||||
|
private String getTokenForQrcodeLogin() {
|
||||||
|
Map<String, String> params = new HashMap<>();
|
||||||
|
params.put("client_id", "381");
|
||||||
|
params.put("v", "1.2");
|
||||||
|
params.put("request_id", UUID.randomUUID().toString());
|
||||||
|
OkResult res = OkHttp.post("https://api.open.uc.cn/cas/ajax/getTokenForQrcodeLogin?__dt=" + RandomUtils.nextInt(1000, 100000) + "&__t=" + new Date().getTime(), params, new HashMap<>());
|
||||||
|
if (this.cookie.isEmpty()) {
|
||||||
|
List<String> cookies = res.getResp().get("set-Cookie");
|
||||||
|
List<String> cookieList = new ArrayList<>();
|
||||||
|
for (String cookie : cookies) {
|
||||||
|
cookieList.add(cookie.split(";")[0]);
|
||||||
|
}
|
||||||
|
this.cookie = TextUtils.join(";", cookieList);
|
||||||
|
}
|
||||||
|
Map<String, Object> json = Json.parseSafe(res.getBody(), Map.class);
|
||||||
|
if (Objects.equals(json.get("message"), "ok")) {
|
||||||
|
return (String) ((Map<String, Object>) ((Map<String, Object>) json.get("data")).get("members")).get("token");
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取二维码内容
|
||||||
|
* <p>
|
||||||
|
* 此方法用于生成二维码的URL内容该URL用于二维码登录,包含了登录所需的token和客户端信息
|
||||||
|
*
|
||||||
|
* @return 返回包含token的二维码URL字符串
|
||||||
|
*/
|
||||||
|
private String getQrCodeToken() {
|
||||||
|
// 获取用于二维码登录的token
|
||||||
|
String token = getTokenForQrcodeLogin();
|
||||||
|
// 组装二维码URL,包含token和客户端标识
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startFlow() {
|
||||||
|
Init.run(this::showInput);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showInput() {
|
||||||
|
try {
|
||||||
|
int margin = ResUtil.dp2px(16);
|
||||||
|
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
|
FrameLayout frame = new FrameLayout(Init.context());
|
||||||
|
params.setMargins(margin, margin, margin, margin);
|
||||||
|
EditText input = new EditText(Init.context());
|
||||||
|
frame.addView(input, params);
|
||||||
|
dialog = new AlertDialog.Builder(Init.getActivity()).setTitle("请输入UC cookie").setView(frame).setNeutralButton("UC二维码", (dialog, which) -> onNeutral()).setNegativeButton(android.R.string.cancel, null).setPositiveButton(android.R.string.ok, (dialog, which) -> onPositive(input.getText().toString())).show();
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onNeutral() {
|
||||||
|
dismiss();
|
||||||
|
Init.execute(this::getQRCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onPositive(String text) {
|
||||||
|
dismiss();
|
||||||
|
Init.execute(() -> {
|
||||||
|
if (text.startsWith("http")) setToken(OkHttp.string(text));
|
||||||
|
else setToken(text);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void getQRCode() {
|
||||||
|
String token = getQrCodeToken();
|
||||||
|
|
||||||
|
Init.run(() -> openApp(token));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void openApp(String token) {
|
||||||
|
try {
|
||||||
|
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||||
|
intent.setClassName("com.alicloud.databox", "com.taobao.login4android.scan.QrScanActivity");
|
||||||
|
intent.putExtra("key_scanParam", token);
|
||||||
|
Init.getActivity().startActivity(intent);
|
||||||
|
} catch (Exception e) {
|
||||||
|
showQRCode("https://su.uc.cn/1_n0ZCv?uc_param_str=dsdnfrpfbivesscpgimibtbmnijblauputogpintnwktprchmt&token=" + token + "&client_id=381&uc_biz_str=S%3Acustom%7CC%3Atitlebar_fix");
|
||||||
|
} finally {
|
||||||
|
Map<String, String> map = new HashMap<>();
|
||||||
|
map.put("token", token);
|
||||||
|
Init.execute(() -> startService(map));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showQRCode(String content) {
|
||||||
|
try {
|
||||||
|
int size = ResUtil.dp2px(240);
|
||||||
|
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(size, size);
|
||||||
|
ImageView image = new ImageView(Init.context());
|
||||||
|
image.setScaleType(ImageView.ScaleType.CENTER_CROP);
|
||||||
|
image.setImageBitmap(QRCode.getBitmap(content, size, 2));
|
||||||
|
FrameLayout frame = new FrameLayout(Init.context());
|
||||||
|
params.gravity = Gravity.CENTER;
|
||||||
|
frame.addView(image, params);
|
||||||
|
dialog = new AlertDialog.Builder(Init.getActivity()).setView(frame).setOnCancelListener(this::dismiss).setOnDismissListener(this::dismiss).show();
|
||||||
|
dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
|
||||||
|
Notify.show("请使用uc网盘App扫描二维码");
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startService(Map<String, String> params) {
|
||||||
|
SpiderDebug.log("----startservice");
|
||||||
|
params.put("client_id", "381");
|
||||||
|
params.put("v", "1.2");
|
||||||
|
params.put("request_id", UUID.randomUUID().toString());
|
||||||
|
service = Executors.newScheduledThreadPool(1);
|
||||||
|
|
||||||
|
service.scheduleWithFixedDelay(() -> {
|
||||||
|
SpiderDebug.log("----scheduleAtFixedRate" + new Date().toString());
|
||||||
|
String result = OkHttp.string("https://api.open.uc.cn/cas/ajax/getServiceTicketByQrcodeToken?__dt=" + RandomUtils.nextInt(1000, 100000) + "&__t=" + new Date().getTime(), params, getWebHeaders());
|
||||||
|
Map<String, Object> json = Json.parseSafe(result, Map.class);
|
||||||
|
if (json.get("status").equals(new Double(2000000))) {
|
||||||
|
setToken((String) ((Map<String, Object>) ((Map<String, Object>) json.get("data")).get("members")).get("service_ticket"));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}, 1, 3, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setToken(String value) {
|
||||||
|
this.serviceTicket = value;
|
||||||
|
SpiderDebug.log("ServiceTicket:" + value);
|
||||||
|
Notify.show("ServiceTicket:" + value);
|
||||||
|
initUserInfo();
|
||||||
|
stopService();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void stopService() {
|
||||||
|
if (service != null) service.shutdownNow();
|
||||||
|
|
||||||
|
|
||||||
|
Init.run(this::dismiss);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void dismiss(DialogInterface dialog) {
|
||||||
|
stopService();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void dismiss() {
|
||||||
|
try {
|
||||||
|
if (dialog != null) dialog.dismiss();
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean getVip() throws Exception {
|
||||||
|
Map<String, Object> listData = Json.parseSafe(api("member?pr=ucpro&fr=pc&uc_param_str=&fetch_subscribe=true&_ch=home&fetch_identity=true", null, null, 0, "GET"), Map.class);
|
||||||
|
return false;//((Map<String, String>) listData.get("data")).get("member_type").contains("VIP");
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getPlayFormatList() {
|
||||||
|
if (this.isVip) {
|
||||||
|
return Arrays.asList("4K", "超清", "高清", "普画");
|
||||||
|
} else {
|
||||||
|
return Collections.singletonList("普画");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> getPlayFormatUcList() {
|
||||||
|
if (this.isVip) {
|
||||||
|
return Arrays.asList("4k", "2k", "super", "high", "normal", "low");
|
||||||
|
} else {
|
||||||
|
return Collections.singletonList("low");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ShareData getShareData(String url) {
|
||||||
|
Pattern pattern = Pattern.compile("https://drive\\.uc\\.cn/s/([^?]+)");
|
||||||
|
Matcher matcher = pattern.matcher(url);
|
||||||
|
if (matcher.find()) {
|
||||||
|
return new ShareData(matcher.group(1), "0");
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void getShareToken(ShareData shareData) throws Exception {
|
||||||
|
if (!this.shareTokenCache.containsKey(shareData.getShareId())) {
|
||||||
|
this.shareTokenCache.remove(shareData.getShareId());
|
||||||
|
Map<String, Object> shareToken = Json.parseSafe(api("share/sharepage/token?" + this.pr, Collections.emptyMap(), Map.of("pwd_id", shareData.getShareId(), "passcode", shareData.getSharePwd() == null ? "" : shareData.getSharePwd()), 0, "POST"), Map.class);
|
||||||
|
if (shareToken.containsKey("data") && ((Map<String, Object>) shareToken.get("data")).containsKey("stoken")) {
|
||||||
|
this.shareTokenCache.put(shareData.getShareId(), (Map<String, Object>) shareToken.get("data"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Map<String, Object>> listFile(int shareIndex, ShareData shareData, List<Item> videos, List<Item> subtitles, String shareId, String folderId, Integer page) throws Exception {
|
||||||
|
int prePage = 200;
|
||||||
|
page = page != null ? page : 1;
|
||||||
|
|
||||||
|
Map<String, Object> listData = Json.parseSafe(api("share/sharepage/detail?" + this.pr + "&pwd_id=" + shareId + "&stoken=" + encodeURIComponent((String) this.shareTokenCache.get(shareId).get("stoken")) + "&pdir_fid=" + folderId + "&force=0&_page=" + page + "&_size=" + prePage + "&_sort=file_type:asc,file_name:asc", Collections.emptyMap(), Collections.emptyMap(), 0, "GET"), Map.class);
|
||||||
|
if (listData.get("data") == null) return Collections.emptyList();
|
||||||
|
List<Map<String, Object>> items = (List<Map<String, Object>>) ((Map<String, Object>) listData.get("data")).get("list");
|
||||||
|
if (items == null) return Collections.emptyList();
|
||||||
|
List<Map<String, Object>> subDir = new ArrayList<>();
|
||||||
|
for (Map<String, Object> item : items) {
|
||||||
|
if (Boolean.TRUE.equals(item.get("dir"))) {
|
||||||
|
subDir.add(item);
|
||||||
|
} else if (Boolean.TRUE.equals(item.get("file")) && "video".equals(item.get("obj_category"))) {
|
||||||
|
if ((Double) item.get("size") < 1024 * 1024 * 5) continue;
|
||||||
|
item.put("stoken", this.shareTokenCache.get(shareData.getShareId()).get("stoken"));
|
||||||
|
videos.add(Item.objectFrom(item, shareData.getShareId(), shareIndex));
|
||||||
|
} else if ("file".equals(item.get("type")) && this.subtitleExts.contains("." + Util.getExt((String) item.get("file_name")))) {
|
||||||
|
subtitles.add(Item.objectFrom(item, shareData.getShareId(), shareIndex));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (page < Math.ceil((double) ((Map<String, Object>) listData.get("metadata")).get("_total") / prePage)) {
|
||||||
|
List<Map<String, Object>> nextItems = listFile(shareIndex, shareData, videos, subtitles, shareId, folderId, page + 1);
|
||||||
|
items.addAll(nextItems);
|
||||||
|
}
|
||||||
|
for (Map<String, Object> dir : subDir) {
|
||||||
|
List<Map<String, Object>> subItems = listFile(shareIndex, shareData, videos, subtitles, shareId, dir.get("fid").toString(), null);
|
||||||
|
items.addAll(subItems);
|
||||||
|
}
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, Object> findBestLCS(Item mainItem, List<Item> targetItems) {
|
||||||
|
List<Map<String, Object>> results = new ArrayList<>();
|
||||||
|
int bestMatchIndex = 0;
|
||||||
|
for (int i = 0; i < targetItems.size(); i++) {
|
||||||
|
Util.LCSResult currentLCS = Util.lcs(mainItem.getName(), targetItems.get(i).getName());
|
||||||
|
Map<String, Object> result = new HashMap<>();
|
||||||
|
result.put("target", targetItems.get(i));
|
||||||
|
result.put("lcs", currentLCS);
|
||||||
|
results.add(result);
|
||||||
|
if (currentLCS.length > results.get(bestMatchIndex).get("lcs").toString().length()) {
|
||||||
|
bestMatchIndex = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Map<String, Object> bestMatch = results.get(bestMatchIndex);
|
||||||
|
Map<String, Object> finalResult = new HashMap<>();
|
||||||
|
finalResult.put("allLCS", results);
|
||||||
|
finalResult.put("bestMatch", bestMatch);
|
||||||
|
finalResult.put("bestMatchIndex", bestMatchIndex);
|
||||||
|
return finalResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void getFilesByShareUrl(int shareIndex, String shareInfo, List<Item> videos, List<Item> subtitles) throws Exception {
|
||||||
|
ShareData shareData = getShareData((String) shareInfo);
|
||||||
|
if (shareData == null) return;
|
||||||
|
getShareToken(shareData);
|
||||||
|
if (!this.shareTokenCache.containsKey(shareData.getShareId())) return;
|
||||||
|
listFile(shareIndex, shareData, videos, subtitles, shareData.getShareId(), shareData.getFolderId(), 1);
|
||||||
|
if (!subtitles.isEmpty()) {
|
||||||
|
for (Item video : videos) {
|
||||||
|
Map<String, Object> matchSubtitle = findBestLCS(video, subtitles);
|
||||||
|
if (matchSubtitle.get("bestMatch") != null) {
|
||||||
|
video.setSubtitle((String) ((Map<String, Object>) matchSubtitle.get("bestMatch")).get("target"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void clean() {
|
||||||
|
saveFileIdCaches.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void clearSaveDir() throws Exception {
|
||||||
|
|
||||||
|
Map<String, Object> listData = Json.parseSafe(api("file/sort?" + this.pr + "&pdir_fid=" + this.saveDirId + "&_page=1&_size=200&_sort=file_type:asc,updated_at:desc", Collections.emptyMap(), Collections.emptyMap(), 0, "GET"), Map.class);
|
||||||
|
if (listData.get("data") != null && ((List<Map<String, Object>>) ((Map<String, Object>) listData.get("data")).get("list")).size() > 0) {
|
||||||
|
List<String> list = new ArrayList<>();
|
||||||
|
for (Map<String, Object> stringStringMap : ((List<Map<String, Object>>) ((Map<String, Object>) listData.get("data")).get("list"))) {
|
||||||
|
list.add((String) stringStringMap.get("fid"));
|
||||||
|
}
|
||||||
|
api("file/delete?" + this.pr, Collections.emptyMap(), Map.of("action_type", "2", "filelist", Json.toJson(list), "exclude_fids", ""), 0, "POST");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createSaveDir(boolean clean) throws Exception {
|
||||||
|
if (this.saveDirId != null) {
|
||||||
|
if (clean) clearSaveDir();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Object> listData = Json.parseSafe(api("file/sort?" + this.pr + "&pdir_fid=0&_page=1&_size=200&_sort=file_type:asc,updated_at:desc", Collections.emptyMap(), Collections.emptyMap(), 0, "GET"), Map.class);
|
||||||
|
if (listData.get("data") != null) {
|
||||||
|
for (Map<String, Object> item : (List<Map<String, Object>>) ((Map<String, Object>) listData.get("data")).get("list")) {
|
||||||
|
if (this.saveDirName.equals(item.get("file_name"))) {
|
||||||
|
this.saveDirId = item.get("fid").toString();
|
||||||
|
clearSaveDir();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.saveDirId == null) {
|
||||||
|
Map<String, Object> create = Json.parseSafe(api("file?" + this.pr, Collections.emptyMap(), Map.of("pdir_fid", "0", "file_name", this.saveDirName, "dir_path", "", "dir_init_lock", "false"), 0, "POST"), Map.class);
|
||||||
|
if (create.get("data") != null && ((Map<String, Object>) create.get("data")).get("fid") != null) {
|
||||||
|
this.saveDirId = ((Map<String, Object>) create.get("data")).get("fid").toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String save(String shareId, String stoken, String fileId, String fileToken, boolean clean) throws Exception {
|
||||||
|
createSaveDir(clean);
|
||||||
|
if (clean) {
|
||||||
|
clean();
|
||||||
|
}
|
||||||
|
if (this.saveDirId == null) return null;
|
||||||
|
if (stoken == null) {
|
||||||
|
getShareToken(new ShareData(shareId, null));
|
||||||
|
if (!this.shareTokenCache.containsKey(shareId)) return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Object> saveResult = Json.parseSafe(api("share/sharepage/save?" + this.pr, null, Map.of("fid_list", List.of(fileId), "fid_token_list", List.of(fileToken), "to_pdir_fid", this.saveDirId, "pwd_id", shareId, "stoken", stoken != null ? stoken : (String) this.shareTokenCache.get(shareId).get("stoken"), "pdir_fid", "0", "scene", "link"), 0, "POST"), Map.class);
|
||||||
|
if (saveResult.get("data") != null && ((Map<Object, Object>) saveResult.get("data")).get("task_id") != null) {
|
||||||
|
int retry = 0;
|
||||||
|
while (true) {
|
||||||
|
|
||||||
|
Map<String, Object> taskResult = Json.parseSafe(api("task?" + this.pr + "&task_id=" + ((Map<String, Object>) saveResult.get("data")).get("task_id") + "&retry_index=" + retry, Collections.emptyMap(), Collections.emptyMap(), 0, "GET"), Map.class);
|
||||||
|
if (taskResult.get("data") != null && ((Map<Object, Object>) taskResult.get("data")).get("save_as") != null && ((Map<Object, Object>) ((Map<Object, Object>) taskResult.get("data")).get("save_as")).get("save_as_top_fids") != null && ((List<String>) ((Map<String, Object>) ((Map<String, Object>) taskResult.get("data")).get("save_as")).get("save_as_top_fids")).size() > 0) {
|
||||||
|
return ((List<String>) ((Map<String, Object>) ((Map<Object, Object>) taskResult.get("data")).get("save_as")).get("save_as_top_fids")).get(0);
|
||||||
|
}
|
||||||
|
retry++;
|
||||||
|
if (retry > 2) break;
|
||||||
|
Thread.sleep(1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getLiveTranscoding(String shareId, String stoken, String fileId, String fileToken, String flag) throws Exception {
|
||||||
|
if (!this.saveFileIdCaches.containsKey(fileId)) {
|
||||||
|
String saveFileId = save(shareId, stoken, fileId, fileToken, true);
|
||||||
|
if (saveFileId == null) return null;
|
||||||
|
this.saveFileIdCaches.put(fileId, saveFileId);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Object> transcoding = Json.parseSafe(api("file/v2/play?" + this.pr, Collections.emptyMap(), Map.of("fid", this.saveFileIdCaches.get(fileId), "resolutions", "normal,low,high,super,2k,4k", "supports", "fmp4"), 0, "POST"), Map.class);
|
||||||
|
if (transcoding.get("data") != null && ((Map<Object, Object>) transcoding.get("data")).get("video_list") != null) {
|
||||||
|
String flagId = flag.split("-")[flag.split("-").length - 1];
|
||||||
|
int index = Util.findAllIndexes(getPlayFormatList(), flagId);
|
||||||
|
String ucFormat = getPlayFormatUcList().get(index);
|
||||||
|
for (Map<String, Object> video : (List<Map<String, Object>>) ((Map<Object, Object>) transcoding.get("data")).get("video_list")) {
|
||||||
|
if (video.get("resolution").equals(ucFormat)) {
|
||||||
|
return (String) ((Map<String, Object>) video.get("video_info")).get("url");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (String) ((Map<String, Object>) ((List<Map<String, Object>>) ((Map<Object, Object>) transcoding.get("data")).get("video_list")).get(index).get("video_info")).get("url");
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getDownload(String shareId, String stoken, String fileId, String fileToken, boolean clean) throws Exception {
|
||||||
|
if (!this.saveFileIdCaches.containsKey(fileId)) {
|
||||||
|
String saveFileId = save(shareId, stoken, fileId, fileToken, clean);
|
||||||
|
if (saveFileId == null) return null;
|
||||||
|
this.saveFileIdCaches.put(fileId, saveFileId);
|
||||||
|
}
|
||||||
|
|
||||||
|
//token不为空
|
||||||
|
if (StringUtils.isNoneBlank(cookieToken)) {
|
||||||
|
SpiderDebug.log("cookieToken不为空: " + cookieToken + ";开始下载");
|
||||||
|
return qrCodeHandler.download(cookieToken, this.saveFileIdCaches.get(fileId));
|
||||||
|
} else {
|
||||||
|
Map<String, Object> down = Json.parseSafe(api("file/download?" + this.pr + "&uc_param_str=", Collections.emptyMap(), Map.of("fids", List.of(this.saveFileIdCaches.get(fileId))), 0, "POST"), Map.class);
|
||||||
|
if (down.get("data") != null) {
|
||||||
|
return ((List<Map<String, Object>>) down.get("data")).get(0).get("download_url").toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper method to convert bytes to hex string
|
||||||
|
private String bytesToHex(byte[] bytes) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (byte b : bytes) {
|
||||||
|
sb.append(String.format("%02x", b));
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encoding helper method
|
||||||
|
private String encodeURIComponent(String value) {
|
||||||
|
try {
|
||||||
|
return java.net.URLEncoder.encode(value, "UTF-8");
|
||||||
|
} catch (Exception e) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,383 @@
|
||||||
|
package com.github.catvod.api;
|
||||||
|
|
||||||
|
import android.app.AlertDialog;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.graphics.drawable.ColorDrawable;
|
||||||
|
import android.view.Gravity;
|
||||||
|
import android.widget.FrameLayout;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import com.github.catvod.bean.uc.Cache;
|
||||||
|
import com.github.catvod.bean.uc.User;
|
||||||
|
import com.github.catvod.crawler.SpiderDebug;
|
||||||
|
import com.github.catvod.net.OkHttp;
|
||||||
|
import com.github.catvod.net.OkResult;
|
||||||
|
import com.github.catvod.spider.Init;
|
||||||
|
import com.github.catvod.utils.*;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
public class UCTokenHandler {
|
||||||
|
private static final String CLIENT_ID = "5acf882d27b74502b7040b0c65519aa7";
|
||||||
|
private static final String SIGN_KEY = "l3srvtd7p42l0d0x1u8d7yc8ye9kki4d";
|
||||||
|
private static final String API_URL = "https://open-api-drive.uc.cn";
|
||||||
|
private static final String CODE_API_URL = "http://api.extscreen.com/ucdrive";
|
||||||
|
|
||||||
|
|
||||||
|
private Map<String, Object> platformStates = new HashMap<>();
|
||||||
|
private Map<String, String> addition = new HashMap<>();
|
||||||
|
private Map<String, String> conf = new HashMap<>();
|
||||||
|
|
||||||
|
|
||||||
|
private ScheduledExecutorService service;
|
||||||
|
private AlertDialog dialog;
|
||||||
|
private final Cache cache;
|
||||||
|
|
||||||
|
public File getCache() {
|
||||||
|
return Path.tv("uctoken");
|
||||||
|
}
|
||||||
|
|
||||||
|
public UCTokenHandler() {
|
||||||
|
addition.put("DeviceID", "07b48aaba8a739356ab8107b5e230ad4");
|
||||||
|
conf.put("api", API_URL);
|
||||||
|
conf.put("clientID", CLIENT_ID);
|
||||||
|
conf.put("signKey", SIGN_KEY);
|
||||||
|
conf.put("appVer", "1.6.8");
|
||||||
|
conf.put("channel", "UCTVOFFICIALWEB");
|
||||||
|
conf.put("codeApi", CODE_API_URL);
|
||||||
|
cache = Cache.objectFrom(Path.read(getCache()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String generateUUID() {
|
||||||
|
return UUID.randomUUID().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String generateDeviceID(String timestamp) {
|
||||||
|
|
||||||
|
return Util.MD5(timestamp).substring(0, 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String generateReqId(String deviceID, String timestamp) {
|
||||||
|
|
||||||
|
return Util.MD5(deviceID + timestamp).substring(0, 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String generateXPanToken(String method, String pathname, String timestamp, String key) {
|
||||||
|
|
||||||
|
return Util.sha256Hex(method + "&" + pathname + "&" + timestamp + "&" + key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String startUC_TOKENScan() throws Exception {
|
||||||
|
String pathname = "/oauth/authorize";
|
||||||
|
String timestamp = String.valueOf(new Date().getTime() / 1000 + 1) + "000";
|
||||||
|
String deviceID = StringUtils.isNoneBlank((String) addition.get("DeviceID")) ? (String) addition.get("DeviceID") : generateDeviceID(timestamp);
|
||||||
|
String reqId = generateReqId(deviceID, timestamp);
|
||||||
|
String token = generateXPanToken("GET", pathname, timestamp, (String) conf.get("signKey"));
|
||||||
|
|
||||||
|
Map<String, String> headers = new HashMap<>();
|
||||||
|
headers.put("Accept", "application/json, text/plain, */*");
|
||||||
|
headers.put("User-Agent", "Mozilla/5.0 (Linux; U; Android 13; zh-cn; M2004J7AC Build/UKQ1.231108.001) AppleWebKit/533.1 (KHTML, like Gecko) Mobile Safari/533.1");
|
||||||
|
headers.put("x-pan-tm", timestamp);
|
||||||
|
headers.put("x-pan-token", token);
|
||||||
|
headers.put("x-pan-client-id", (String) conf.get("clientID"));
|
||||||
|
headers.put("Host", "open-api-drive.uc.cn");
|
||||||
|
Map<String, String> params = new HashMap<>();
|
||||||
|
params.put("req_id", reqId);
|
||||||
|
params.put("access_token", StringUtils.isNoneBlank((String) addition.get("AccessToken")) ? (String) addition.get("AccessToken") : "");
|
||||||
|
params.put("app_ver", (String) conf.get("appVer"));
|
||||||
|
params.put("device_id", deviceID);
|
||||||
|
params.put("device_brand", "Xiaomi");
|
||||||
|
params.put("platform", "tv");
|
||||||
|
params.put("device_name", "M2004J7AC");
|
||||||
|
params.put("device_model", "M2004J7AC");
|
||||||
|
params.put("build_device", "M2004J7AC");
|
||||||
|
params.put("build_product", "M2004J7AC");
|
||||||
|
params.put("device_gpu", "Adreno (TM) 550");
|
||||||
|
params.put("activity_rect", URLEncoder.encode("{}", "UTF-8"));
|
||||||
|
params.put("channel", (String) conf.get("channel"));
|
||||||
|
params.put("auth_type", "code");
|
||||||
|
params.put("client_id", (String) conf.get("clientID"));
|
||||||
|
params.put("scope", "netdisk");
|
||||||
|
params.put("qrcode", "1");
|
||||||
|
params.put("qr_width", "460");
|
||||||
|
params.put("qr_height", "460");
|
||||||
|
OkResult okResult = OkHttp.get(API_URL + pathname, params, headers);
|
||||||
|
|
||||||
|
|
||||||
|
JsonObject resData = Json.safeObject(okResult.getBody());
|
||||||
|
String queryToken = resData.get("query_token").getAsString();
|
||||||
|
String qrCode = resData.get("qr_data").getAsString();
|
||||||
|
|
||||||
|
platformStates.put("UC_TOKEN", new HashMap<String, String>() {{
|
||||||
|
put("query_token", queryToken);
|
||||||
|
put("request_id", reqId);
|
||||||
|
}});
|
||||||
|
Init.run(() -> showQRCode(qrCode));
|
||||||
|
|
||||||
|
Init.execute(this::startService);
|
||||||
|
/*Map<String, Object> result = new HashMap<>();
|
||||||
|
result.put("qrcode", "data:image/png;base64," + qrCode);
|
||||||
|
result.put("status", "NEW");*/
|
||||||
|
return qrCode;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Map<String, Object> checkUC_TOKENStatus() throws UnsupportedEncodingException {
|
||||||
|
Map<String, String> state = (Map<String, String>) platformStates.get("UC_TOKEN");
|
||||||
|
if (state == null) {
|
||||||
|
return Map.of("status", "EXPIRED");
|
||||||
|
}
|
||||||
|
|
||||||
|
String pathname = "/oauth/code";
|
||||||
|
String timestamp = String.valueOf(new Date().getTime() / 1000 + 1) + "000";
|
||||||
|
String deviceID = StringUtils.isAllBlank((String) addition.get("DeviceID")) ? (String) addition.get("DeviceID") : generateDeviceID(timestamp);
|
||||||
|
String reqId = generateReqId(deviceID, timestamp);
|
||||||
|
String xPanToken = generateXPanToken("GET", pathname, timestamp, (String) conf.get("signKey"));
|
||||||
|
|
||||||
|
Map<String, String> headers = new HashMap<>();
|
||||||
|
headers.put("Accept", "application/json, text/plain, */*");
|
||||||
|
headers.put("User-Agent", "Mozilla/5.0 (Linux; U; Android 13; zh-cn; M2004J7AC Build/UKQ1.231108.001) AppleWebKit/533.1 (KHTML, like Gecko) Mobile Safari/533.1");
|
||||||
|
headers.put("x-pan-tm", timestamp);
|
||||||
|
headers.put("x-pan-token", xPanToken);
|
||||||
|
headers.put("x-pan-client-id", (String) conf.get("clientID"));
|
||||||
|
|
||||||
|
Map<String, String> params = new HashMap<>();
|
||||||
|
params.put("req_id", reqId);
|
||||||
|
params.put("access_token", (String) addition.get("AccessToken"));
|
||||||
|
params.put("app_ver", (String) conf.get("appVer"));
|
||||||
|
params.put("device_id", deviceID);
|
||||||
|
params.put("device_brand", "Xiaomi");
|
||||||
|
params.put("platform", "tv");
|
||||||
|
params.put("device_name", "M2004J7AC");
|
||||||
|
params.put("device_model", "M2004J7AC");
|
||||||
|
params.put("build_device", "M2004J7AC");
|
||||||
|
params.put("build_product", "M2004J7AC");
|
||||||
|
params.put("device_gpu", "Adreno (TM) 550");
|
||||||
|
params.put("activity_rect", URLEncoder.encode("{}", "UTF-8"));
|
||||||
|
params.put("channel", (String) conf.get("channel"));
|
||||||
|
params.put("client_id", (String) conf.get("clientID"));
|
||||||
|
params.put("scope", "netdisk");
|
||||||
|
params.put("query_token", state.get("query_token"));
|
||||||
|
|
||||||
|
OkResult okResult = OkHttp.get(API_URL + pathname, params, headers);
|
||||||
|
|
||||||
|
//扫码成功
|
||||||
|
if (okResult.getCode() == 200) {
|
||||||
|
JsonObject resData = Json.safeObject(okResult.getBody());
|
||||||
|
String code = resData.get("code").getAsString();
|
||||||
|
|
||||||
|
OkResult okResult1 = getAccessToken(code, false);
|
||||||
|
|
||||||
|
|
||||||
|
if (okResult1.getCode() == 200) {
|
||||||
|
JsonObject tokenResData = Json.safeObject(okResult1.getBody());
|
||||||
|
platformStates.remove("UC_TOKEN");
|
||||||
|
Map<String, Object> result = new HashMap<>();
|
||||||
|
result.put("status", "CONFIRMED");
|
||||||
|
result.put("cookie", tokenResData.get("data").getAsJsonObject().get("access_token").getAsString());
|
||||||
|
SpiderDebug.log("uc Token获取成功:" + tokenResData.get("data").getAsJsonObject().get("access_token").getAsString());
|
||||||
|
|
||||||
|
//保存到本地
|
||||||
|
cache.setTokenUser(User.objectFrom(Json.toJson(tokenResData.get("data").getAsJsonObject())));
|
||||||
|
//停止检验线程,关闭弹窗
|
||||||
|
stopService();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (okResult.getCode() == 400) {
|
||||||
|
SpiderDebug.log("uc Token获取失败:" + okResult.getBody());
|
||||||
|
|
||||||
|
return Map.of("status", "NEW");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
platformStates.remove("UC_TOKEN");
|
||||||
|
return Map.of("status", "EXPIRED");
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 获取访问令牌或者刷新令牌
|
||||||
|
*
|
||||||
|
* @param code
|
||||||
|
* @param refresh 是否刷新,如果是,code为refresh_token
|
||||||
|
* @return
|
||||||
|
* @throws UnsupportedEncodingException
|
||||||
|
*/
|
||||||
|
public OkResult getAccessToken(String code, boolean refresh) {
|
||||||
|
|
||||||
|
String timestamp = String.valueOf(new Date().getTime() / 1000 + 1) + "000";
|
||||||
|
String deviceID = StringUtils.isAllBlank((String) addition.get("DeviceID")) ? (String) addition.get("DeviceID") : generateDeviceID(timestamp);
|
||||||
|
Map<String, String> headers = new HashMap<>();
|
||||||
|
headers.put("Accept", "application/json, text/plain, */*");
|
||||||
|
headers.put("User-Agent", "Mozilla/5.0 (Linux; U; Android 13; zh-cn; M2004J7AC Build/UKQ1.231108.001) AppleWebKit/533.1 (KHTML, like Gecko) Mobile Safari/533.1");
|
||||||
|
|
||||||
|
String pathname = "/token";
|
||||||
|
String reqId = generateReqId(deviceID, timestamp);
|
||||||
|
|
||||||
|
Map<String, String> postData = new HashMap<>();
|
||||||
|
postData.put("req_id", reqId);
|
||||||
|
postData.put("app_ver", (String) conf.get("appVer"));
|
||||||
|
postData.put("device_id", deviceID);
|
||||||
|
postData.put("device_brand", "Xiaomi");
|
||||||
|
postData.put("platform", "tv");
|
||||||
|
postData.put("device_name", "M2004J7AC");
|
||||||
|
postData.put("device_model", "M2004J7AC");
|
||||||
|
postData.put("build_device", "M2004J7AC");
|
||||||
|
postData.put("build_product", "M2004J7AC");
|
||||||
|
postData.put("device_gpu", "Adreno (TM) 550");
|
||||||
|
try {
|
||||||
|
postData.put("activity_rect", URLEncoder.encode("{}", "UTF-8"));
|
||||||
|
} catch (Exception e) {
|
||||||
|
|
||||||
|
SpiderDebug.log("encode出错" + e.getMessage());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
postData.put("channel", conf.get("channel"));
|
||||||
|
if (refresh) {
|
||||||
|
postData.put("refresh_token", code);
|
||||||
|
SpiderDebug.log("开始刷新uc accesstoken");
|
||||||
|
} else {
|
||||||
|
postData.put("code", code);
|
||||||
|
SpiderDebug.log("开始获取uc accesstoken");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return OkHttp.post(conf.get("codeApi") + pathname, Json.toJson(postData), headers);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 刷新refresh token
|
||||||
|
*
|
||||||
|
* @param refreshToken 刷新token
|
||||||
|
* @return 防火新的accesstoken
|
||||||
|
*/
|
||||||
|
public String refreshToken(String refreshToken) {
|
||||||
|
OkResult okResult1 = this.getAccessToken(refreshToken, true);
|
||||||
|
|
||||||
|
if (okResult1.getCode() == 200) {
|
||||||
|
JsonObject tokenResData = Json.safeObject(okResult1.getBody());
|
||||||
|
SpiderDebug.log("uc Token刷新成功:" + tokenResData.get("data").getAsJsonObject().get("access_token").getAsString());
|
||||||
|
//保存到本地
|
||||||
|
cache.setTokenUser(User.objectFrom(Json.toJson(tokenResData.get("data").getAsJsonObject())));
|
||||||
|
return tokenResData.get("data").getAsJsonObject().get("access_token").getAsString();
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String download(String token, String saveFileId) throws Exception {
|
||||||
|
SpiderDebug.log("开始下载:" + saveFileId + ";token:" + token);
|
||||||
|
String pathname = "/file";
|
||||||
|
String timestamp = String.valueOf(new Date().getTime() / 1000 + 1) + "000";
|
||||||
|
String deviceID = StringUtils.isAllBlank((String) addition.get("DeviceID")) ? (String) addition.get("DeviceID") : generateDeviceID(timestamp);
|
||||||
|
String reqId = generateReqId(deviceID, timestamp);
|
||||||
|
String xPanToken = generateXPanToken("GET", pathname, timestamp, (String) conf.get("signKey"));
|
||||||
|
|
||||||
|
Map<String, String> headers = new HashMap<>();
|
||||||
|
//headers.put("Accept-Encoding", "gzip");
|
||||||
|
headers.put("content-type", "text/plain;charset=UTF-8");
|
||||||
|
headers.put("User-Agent", "Mozilla/5.0 (Linux; U; Android 13; zh-cn; M2004J7AC Build/UKQ1.231108.001) AppleWebKit/533.1 (KHTML, like Gecko) Mobile Safari/533.1");
|
||||||
|
headers.put("x-pan-tm", timestamp);
|
||||||
|
headers.put("x-pan-token", xPanToken);
|
||||||
|
headers.put("x-pan-client-id", (String) conf.get("clientID"));
|
||||||
|
|
||||||
|
|
||||||
|
Map<String, String> params = new HashMap<>();
|
||||||
|
params.put("req_id", reqId);
|
||||||
|
params.put("access_token", token);
|
||||||
|
params.put("app_ver", (String) conf.get("appVer"));
|
||||||
|
params.put("device_id", deviceID);
|
||||||
|
params.put("device_brand", "Xiaomi");
|
||||||
|
params.put("platform", "tv");
|
||||||
|
params.put("device_name", "M2004J7AC");
|
||||||
|
params.put("device_model", "M2004J7AC");
|
||||||
|
params.put("build_device", "M2004J7AC");
|
||||||
|
params.put("build_product", "M2004J7AC");
|
||||||
|
params.put("device_gpu", "Adreno (TM) 550");
|
||||||
|
params.put("activity_rect", URLEncoder.encode("{}", "UTF-8"));
|
||||||
|
params.put("channel", (String) conf.get("channel"));
|
||||||
|
params.put("method", "streaming");
|
||||||
|
params.put("group_by", "source");
|
||||||
|
params.put("fid", saveFileId);
|
||||||
|
params.put("resolution", "low,normal,high,super,2k,4k");
|
||||||
|
params.put("support", "dolby_vision");
|
||||||
|
|
||||||
|
OkResult okResult1 = OkHttp.get(API_URL + pathname, params, headers);
|
||||||
|
JsonObject obj = Json.safeObject(okResult1.getBody());
|
||||||
|
if (okResult1.getCode() != 200) {
|
||||||
|
Notify.show(obj.get("error_info").getAsString());
|
||||||
|
SpiderDebug.log("uc TV 错误信息:" + obj.get("error_info").getAsString());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String downloadUrl = obj.get("data").getAsJsonObject().get("video_info").getAsJsonArray().get(0).getAsJsonObject().get("url").getAsString();
|
||||||
|
SpiderDebug.log("uc TV 下载文件内容:" + downloadUrl);
|
||||||
|
return downloadUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 显示qrcode
|
||||||
|
*
|
||||||
|
* @param base64Str
|
||||||
|
*/
|
||||||
|
public void showQRCode(String base64Str) {
|
||||||
|
try {
|
||||||
|
int size = ResUtil.dp2px(240);
|
||||||
|
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(size, size);
|
||||||
|
ImageView image = new ImageView(Init.context());
|
||||||
|
image.setScaleType(ImageView.ScaleType.CENTER_CROP);
|
||||||
|
image.setImageBitmap(QRCode.base64StringToImage(base64Str));
|
||||||
|
FrameLayout frame = new FrameLayout(Init.context());
|
||||||
|
params.gravity = Gravity.CENTER;
|
||||||
|
frame.addView(image, params);
|
||||||
|
dialog = new AlertDialog.Builder(Init.getActivity()).setView(frame).setOnCancelListener(this::dismiss).setOnDismissListener(this::dismiss).show();
|
||||||
|
dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
|
||||||
|
Notify.show("请使用uc网盘App扫描二维码");
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void dismiss() {
|
||||||
|
try {
|
||||||
|
if (dialog != null) dialog.dismiss();
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void dismiss(DialogInterface dialog) {
|
||||||
|
stopService();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void stopService() {
|
||||||
|
if (service != null) service.shutdownNow();
|
||||||
|
Init.run(this::dismiss);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void startService() {
|
||||||
|
SpiderDebug.log("----start UC token service");
|
||||||
|
|
||||||
|
service = Executors.newScheduledThreadPool(1);
|
||||||
|
|
||||||
|
service.scheduleWithFixedDelay(() -> {
|
||||||
|
try {
|
||||||
|
SpiderDebug.log("----checkUC_TOKENStatus中");
|
||||||
|
|
||||||
|
checkUC_TOKENStatus();
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}, 1, 3, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,315 @@
|
||||||
|
package com.github.catvod.api;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import com.github.catvod.bean.Result;
|
||||||
|
import com.github.catvod.crawler.SpiderDebug;
|
||||||
|
import com.github.catvod.net.OkHttp;
|
||||||
|
import com.github.catvod.net.OkResult;
|
||||||
|
import com.github.catvod.utils.Json;
|
||||||
|
import com.github.catvod.utils.ProxyServer;
|
||||||
|
import com.github.catvod.utils.ProxyVideo;
|
||||||
|
import com.github.catvod.utils.Util;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
|
||||||
|
import org.apache.commons.codec.binary.Base64;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.security.GeneralSecurityException;
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
import javax.crypto.spec.IvParameterSpec;
|
||||||
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
|
|
||||||
|
public class YunDrive {
|
||||||
|
private final Pattern regex = Pattern.compile("https://yun\\.139\\.com/shareweb/#/w/i/([^&]+)");
|
||||||
|
private final SecretKeySpec secretKey;
|
||||||
|
private final String baseUrl = "https://share-kd-njs.yun.139.com/yun-share/richlifeApp/devapp/IOutLink/";
|
||||||
|
private final Map<String, String> baseHeaders = new HashMap<>();
|
||||||
|
|
||||||
|
private final Map<String, JsonObject> cache = new HashMap<>();
|
||||||
|
|
||||||
|
private static class Loader {
|
||||||
|
static volatile YunDrive INSTANCE = new YunDrive();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static YunDrive get() {
|
||||||
|
return Loader.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public YunDrive() {
|
||||||
|
|
||||||
|
this.secretKey = new SecretKeySpec("PVGDwmcvfs1uV3d1".getBytes(Charset.defaultCharset()), "AES");
|
||||||
|
baseHeaders.put("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36");
|
||||||
|
baseHeaders.put("Content-Type", "application/json");
|
||||||
|
baseHeaders.put("hcy-cool-flag", "1");
|
||||||
|
baseHeaders.put("x-deviceinfo", "||3|12.27.0|chrome|131.0.0.0|5c7c68368f048245e1ce47f1c0f8f2d0||windows 10|1536X695|zh-CN|||");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private String encrypt(String data) throws GeneralSecurityException {
|
||||||
|
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
|
||||||
|
byte[] ivBytes = new byte[16];
|
||||||
|
new SecureRandom().nextBytes(ivBytes);
|
||||||
|
cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(ivBytes));
|
||||||
|
byte[] encrypted = cipher.doFinal(data.getBytes(Charset.defaultCharset()));
|
||||||
|
byte[] combined = new byte[ivBytes.length + encrypted.length];
|
||||||
|
System.arraycopy(ivBytes, 0, combined, 0, ivBytes.length);
|
||||||
|
System.arraycopy(encrypted, 0, combined, ivBytes.length, encrypted.length);
|
||||||
|
return Base64.encodeBase64String(combined);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private String decrypt(String data) throws GeneralSecurityException {
|
||||||
|
byte[] combined = Base64.decodeBase64(data);
|
||||||
|
byte[] ivBytes = Arrays.copyOfRange(combined, 0, 16);
|
||||||
|
byte[] encrypted = Arrays.copyOfRange(combined, 16, combined.length);
|
||||||
|
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
|
||||||
|
cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(ivBytes));
|
||||||
|
return new String(cipher.doFinal(encrypted), Charset.defaultCharset());
|
||||||
|
}
|
||||||
|
|
||||||
|
public String extractLinkID(String url) {
|
||||||
|
String linkID = "";
|
||||||
|
Matcher matcher = regex.matcher(url);
|
||||||
|
boolean finded = matcher.find();
|
||||||
|
if (!finded) {
|
||||||
|
matcher = Pattern.compile("https://caiyun\\.139\\.com/m/i\\?([^&]+)").matcher(url);
|
||||||
|
finded = matcher.find();
|
||||||
|
}
|
||||||
|
if (!finded) {
|
||||||
|
matcher = Pattern.compile("https://yun.139.com/shareweb/#/w/i/([\\w-]+)").matcher(url);
|
||||||
|
finded = matcher.find();
|
||||||
|
}
|
||||||
|
if (!finded) {
|
||||||
|
matcher = Pattern.compile("https://caiyun.139.com/w/i/([\\w-]+)").matcher(url);
|
||||||
|
finded = matcher.find();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (finded) linkID = matcher.group(1);
|
||||||
|
return linkID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JsonObject fetchShareInfo(String pCaID, String linkID) throws IOException, GeneralSecurityException {
|
||||||
|
if (linkID.isEmpty()) throw new IllegalStateException("linkID not initialized");
|
||||||
|
|
||||||
|
String cacheKey = linkID + "-" + pCaID;
|
||||||
|
if (cache.containsKey(cacheKey)) return cache.get(cacheKey);
|
||||||
|
|
||||||
|
Map<String, Object> requestBody = Map.of("getOutLinkInfoReq", Map.of("account", "", "linkID", linkID, "passwd", "", "caSrt", 1, "coSrt", 1, "srtDr", 0, "bNum", 1, "pCaID", pCaID, "eNum", 200), "commonAccountInfo", Map.of("account", "", "accountType", 1));
|
||||||
|
|
||||||
|
|
||||||
|
OkResult okResult = OkHttp.post(baseUrl + "getOutLinkInfoV6", encrypt(Json.toJson(requestBody)), baseHeaders);
|
||||||
|
JsonObject result = Json.safeObject(decrypt(okResult.getBody())).getAsJsonObject("data");
|
||||||
|
cache.put(cacheKey, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, List<Map<String, String>>> processShareData(String url) throws Exception {
|
||||||
|
if (url == null || url.isEmpty()) return Collections.emptyMap();
|
||||||
|
|
||||||
|
boolean isUrl = url.startsWith("http");
|
||||||
|
String pCaID = isUrl ? "root" : url;
|
||||||
|
String linkID = "";
|
||||||
|
if (isUrl) linkID = extractLinkID(url);
|
||||||
|
|
||||||
|
List<Map<String, String>> fileList = fetchFileList(pCaID, linkID);
|
||||||
|
Map<String, List<Map<String, String>>> result = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
for (Map<String, String> item : fileList) {
|
||||||
|
String name = item.get("name");
|
||||||
|
List<Map<String, String>> subItems = fetchUrlList(item.get("path"), linkID);
|
||||||
|
if (!subItems.isEmpty()) {
|
||||||
|
|
||||||
|
List<Map<String, String>> list = result.get(name);
|
||||||
|
if (list == null) {
|
||||||
|
list = new ArrayList<>();
|
||||||
|
result.put(name, list);
|
||||||
|
}
|
||||||
|
list.addAll(subItems);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.isEmpty()) {
|
||||||
|
List<Map<String, String>> rootItems = fetchFileList(url, linkID);
|
||||||
|
List<Map<String, String>> filteredList = new ArrayList<>();
|
||||||
|
for (Map<String, String> m : rootItems) {
|
||||||
|
if (!m.isEmpty()) {
|
||||||
|
filteredList.add(m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.put("root", filteredList);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Map<String, String>> fetchFileList(String pCaID, String linkID) throws Exception {
|
||||||
|
if (pCaID == null) return Collections.emptyList();
|
||||||
|
|
||||||
|
String actualID = pCaID.startsWith("http") ? "root" : pCaID;
|
||||||
|
JsonObject response = fetchShareInfo(actualID, linkID);
|
||||||
|
if (!response.has("caLst")) return Collections.emptyList();
|
||||||
|
|
||||||
|
List<Map<String, String>> items = new ArrayList<>();
|
||||||
|
Pattern filter = Pattern.compile("App|活动中心|免费|1T空间|免流");
|
||||||
|
JsonElement array = response.get("caLst");
|
||||||
|
if (!array.isJsonNull()) {
|
||||||
|
for (JsonElement element : array.getAsJsonArray()) {
|
||||||
|
JsonObject entry = element.getAsJsonObject();
|
||||||
|
String name = entry.get("caName").getAsString();
|
||||||
|
String path = entry.get("path").getAsString();
|
||||||
|
|
||||||
|
if (!filter.matcher(name).find()) {
|
||||||
|
items.add(Map.of("name", name, "path", path));
|
||||||
|
items.addAll(fetchFileList(path, linkID));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Map<String, String>> fetchUrlList(String pCaID, String linkID) throws Exception {
|
||||||
|
JsonObject response = fetchShareInfo(pCaID, linkID);
|
||||||
|
List<Map<String, String>> items = new ArrayList<>();
|
||||||
|
|
||||||
|
if (response.has("coLst")) {
|
||||||
|
for (JsonElement element : response.getAsJsonArray("coLst")) {
|
||||||
|
JsonObject entry = element.getAsJsonObject();
|
||||||
|
if (entry.get("coType").getAsInt() == 3) {
|
||||||
|
items.add(Map.of("name", entry.get("coName").getAsString(), "contentId", entry.get("coID").getAsString(), "linkID", linkID, "path", entry.get("path").getAsString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (response.has("caLst")) {
|
||||||
|
for (JsonElement element : response.getAsJsonArray("caLst")) {
|
||||||
|
items.addAll(fetchUrlList(element.getAsJsonObject().get("path").getAsString(), linkID));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String fetchPlayUrl(String contentId, String linkID) throws Exception {
|
||||||
|
Map<String, Object> requestBody = Map.of("getContentInfoFromOutLinkReq", Map.of("contentId", contentId, "linkID", linkID, "account", ""), "commonAccountInfo", Map.of("account", "", "accountType", 1));
|
||||||
|
|
||||||
|
|
||||||
|
OkResult okResult = OkHttp.post(baseUrl + "getContentInfoFromOutLink", Json.toJson(requestBody), Map.of("Accept-Encoding", "gzip, deflate, br, zstd", "User-Agent", baseHeaders.get("User-Agent")));
|
||||||
|
String m3u8 = Json.safeObject(okResult.getBody()).getAsJsonObject("data").getAsJsonObject("contentInfo").get("presentURL").getAsString();
|
||||||
|
|
||||||
|
String m3u8Str = OkHttp.string(m3u8);
|
||||||
|
String resultUrl = m3u8;
|
||||||
|
for (String s : m3u8Str.split("\n")) {
|
||||||
|
if (s.contains("index.m3u8")) {
|
||||||
|
resultUrl = s;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return m3u8.split("playlist.m3u8")[0] + resultUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String get4kVideoInfo(String fid, String linkID) throws Exception {
|
||||||
|
String auth = getAuth();
|
||||||
|
String phone = getPhone();
|
||||||
|
|
||||||
|
// 构建 JSON 请求体
|
||||||
|
Map<String, Object> requestBody = new HashMap<>();
|
||||||
|
Map<String, Object> dlFromOutLinkReqV3 = new HashMap<>();
|
||||||
|
Map<String, Object> commonAccountInfo = new HashMap<>();
|
||||||
|
|
||||||
|
dlFromOutLinkReqV3.put("linkID", linkID);
|
||||||
|
dlFromOutLinkReqV3.put("account", phone);
|
||||||
|
|
||||||
|
Map<String, Object> coIDLst = new HashMap<>();
|
||||||
|
coIDLst.put("item", Collections.singletonList(fid));
|
||||||
|
dlFromOutLinkReqV3.put("coIDLst", coIDLst);
|
||||||
|
|
||||||
|
commonAccountInfo.put("account", phone);
|
||||||
|
commonAccountInfo.put("accountType", 1);
|
||||||
|
|
||||||
|
requestBody.put("dlFromOutLinkReqV3", dlFromOutLinkReqV3);
|
||||||
|
requestBody.put("commonAccountInfo", commonAccountInfo);
|
||||||
|
|
||||||
|
/* {
|
||||||
|
"dlFromOutLinkReqV3" : {
|
||||||
|
"linkID" : "105CpbaJQFYc6",
|
||||||
|
"account" : "18896781601",
|
||||||
|
"coIDLst" : {
|
||||||
|
"item" : [ "DFTOdJkuAEwA1011ZpAcj1pl039202404112124392cb/Fkco6TgMKlnJwKbVul0ZKeYT5p2hIioVy" ]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"commonAccountInfo" : {
|
||||||
|
"account" : "18896781601",
|
||||||
|
"accountType" : 1
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
|
||||||
|
// 构建请求
|
||||||
|
Map<String, String> header = getHeader();
|
||||||
|
|
||||||
|
|
||||||
|
OkResult okResult = OkHttp.post(baseUrl + "dlFromOutLinkV3", encrypt(Json.toJson(requestBody)), header);
|
||||||
|
JsonObject resultJson = Json.safeObject(decrypt(okResult.getBody()));
|
||||||
|
if (resultJson.get("resultCode").getAsInt() == 0) {
|
||||||
|
return resultJson.getAsJsonObject("data").get("redrUrl").getAsString();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析 JSON 响应
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getPhone() {
|
||||||
|
String phone = StringUtils.split(Util.base64Decode(getAuth()), ":")[1];
|
||||||
|
SpiderDebug.log("phone:" + phone);
|
||||||
|
return phone;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getAuth() {
|
||||||
|
String auth = YunTokenHandler.get().getToken();
|
||||||
|
SpiderDebug.log("auth:" + auth);
|
||||||
|
return auth;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private static Map<String, String> getHeader() {
|
||||||
|
Map<String, String> header = new HashMap<>();
|
||||||
|
|
||||||
|
header.put("X-Deviceinfo", "||3|12.27.0|safari|13.1.2|1||macos 10.15.6|1324X381|zh-cn|||");
|
||||||
|
header.put("hcy-cool-flag", "1");
|
||||||
|
header.put("Authorization", "Basic " + getAuth());
|
||||||
|
header.put("Content-Type", "application/json");
|
||||||
|
return header;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public String playerContent(String[] split, String flag) throws Exception {
|
||||||
|
String playUrl = "";
|
||||||
|
if (flag.contains("原画")) {
|
||||||
|
String contentId = split[0];
|
||||||
|
String linkID = split[1];
|
||||||
|
playUrl = YunDrive.get().get4kVideoInfo(contentId, linkID);
|
||||||
|
playUrl = ProxyServer.INSTANCE.buildProxyUrl(playUrl, new HashMap<>());
|
||||||
|
|
||||||
|
} else {
|
||||||
|
String contentId = split[0];
|
||||||
|
String linkID = split[1];
|
||||||
|
playUrl = YunDrive.get().fetchPlayUrl(contentId, linkID);
|
||||||
|
}
|
||||||
|
return Result.get().url(playUrl).octet().header(getHeader()).string();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
package com.github.catvod.api;
|
||||||
|
|
||||||
|
|
||||||
|
import com.github.catvod.bean.yun.Cache;
|
||||||
|
import com.github.catvod.bean.yun.User;
|
||||||
|
import com.github.catvod.utils.Path;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
public class YunTokenHandler {
|
||||||
|
|
||||||
|
private final Cache cache;
|
||||||
|
|
||||||
|
public File getCache() {
|
||||||
|
return Path.tv("yun139");
|
||||||
|
}
|
||||||
|
|
||||||
|
private YunTokenHandler() {
|
||||||
|
cache = Cache.objectFrom(Path.read(getCache()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Loader {
|
||||||
|
static volatile YunTokenHandler INSTANCE = new YunTokenHandler();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static YunTokenHandler get() {
|
||||||
|
return YunTokenHandler.Loader.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public String getToken() {
|
||||||
|
User user = cache.getUser();
|
||||||
|
return user.getCookie();
|
||||||
|
//return "cGM6MTg4OTY3ODE2MDE6eTM1Tjd1dG58MXxSQ1N8MTc1NDQ2OTgwNzEyOXxzMlN0T1VEV3lOVmF5V3pNbGFfM2tJbVp1ZmlqSHBqaEhTSzVyNHZqVXNRLmlhV3loSUxHNDFkMUI5N1BqXzhWN0dtVWtKLnBTclhpNGpZU1EuTGZWMTV3MVFoZmNpcEVoZkxUV2tvYjB0bkFTYV9RTUhhaHhveWx6YkdmcEhQdjNCS1lrbnp1LkxaWDdKOE40YkNNRjkzT3piNmx2Y0d3TWdVUkl5b18ubVUt";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
package com.github.catvod.bean.BD;
|
||||||
|
|
||||||
|
|
||||||
|
import com.github.catvod.api.BaiDuYunHandler;
|
||||||
|
import com.github.catvod.bean.yun.User;
|
||||||
|
import com.github.catvod.spider.Init;
|
||||||
|
import com.github.catvod.utils.Path;
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
public class Cache {
|
||||||
|
|
||||||
|
@SerializedName("user")
|
||||||
|
private User user;
|
||||||
|
|
||||||
|
|
||||||
|
public static Cache objectFrom(String str) {
|
||||||
|
Cache item = new Gson().fromJson(str, Cache.class);
|
||||||
|
return item == null ? new Cache() : item;
|
||||||
|
}
|
||||||
|
|
||||||
|
public User getUser() {
|
||||||
|
return user == null ? new User("") : user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUser(User user) {
|
||||||
|
this.user = user;
|
||||||
|
this.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void save() {
|
||||||
|
Init.execute(() -> Path.write(BaiDuYunHandler.get().getCache(), toString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return new Gson().toJson(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -58,7 +58,9 @@ public class Result {
|
||||||
public static String string(List<Class> classes, List<Vod> list, LinkedHashMap<String, List<Filter>> filters) {
|
public static String string(List<Class> classes, List<Vod> list, LinkedHashMap<String, List<Filter>> filters) {
|
||||||
return Result.get().classes(classes).vod(list).filters(filters).string();
|
return Result.get().classes(classes).vod(list).filters(filters).string();
|
||||||
}
|
}
|
||||||
|
public static String string(Integer page,Integer pagecount,Integer limit,Integer total,List<Vod> list){
|
||||||
|
return Result.get().page(page,pagecount,limit,total).vod(list).string();
|
||||||
|
}
|
||||||
public static String string(List<Class> classes, List<Vod> list, JSONObject filters) {
|
public static String string(List<Class> classes, List<Vod> list, JSONObject filters) {
|
||||||
return Result.get().classes(classes).vod(list).filters(filters).string();
|
return Result.get().classes(classes).vod(list).filters(filters).string();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,9 @@ package com.github.catvod.bean;
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.google.gson.annotations.SerializedName;
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class Vod {
|
public class Vod {
|
||||||
|
|
||||||
@SerializedName("type_name")
|
@SerializedName("type_name")
|
||||||
|
|
@ -200,4 +203,46 @@ public class Vod {
|
||||||
this.ratio = ratio;
|
this.ratio = ratio;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public static class VodPlayBuilder{
|
||||||
|
private List<String> vodPlayFrom = new ArrayList<String>();
|
||||||
|
private List<String> vodPlayUrl = new ArrayList<String>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式: from name1$$$name2$$$name3
|
||||||
|
* url name$url#name2$url2$$$(分类2)$$分类3
|
||||||
|
* @param playFrom
|
||||||
|
* @param playUrl
|
||||||
|
*/
|
||||||
|
public VodPlayBuilder append(String playFrom, List<PlayUrl> playUrl){
|
||||||
|
vodPlayFrom.add(playFrom);
|
||||||
|
vodPlayUrl.add(toPlayUrlStr(playUrl));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BuildResult build(){
|
||||||
|
BuildResult buildResult = new BuildResult();
|
||||||
|
buildResult.vodPlayFrom = String.join("$$$", vodPlayFrom);
|
||||||
|
buildResult.vodPlayUrl = String.join("$$$", vodPlayUrl);
|
||||||
|
return buildResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String toPlayUrlStr(List<PlayUrl> playUrl) {
|
||||||
|
List<String> list = new ArrayList<>();
|
||||||
|
for (PlayUrl url : playUrl) {
|
||||||
|
list.add(url.name.replace("m3u8", "") + '$' + url.url);
|
||||||
|
}
|
||||||
|
return String.join("#", list);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class BuildResult{
|
||||||
|
public String vodPlayFrom;
|
||||||
|
public String vodPlayUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class PlayUrl {
|
||||||
|
public String flag; // 线路标志
|
||||||
|
public String name;
|
||||||
|
public String url;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,13 +6,16 @@ import com.github.catvod.bean.Vod;
|
||||||
import com.github.catvod.utils.Util;
|
import com.github.catvod.utils.Util;
|
||||||
import com.google.gson.annotations.SerializedName;
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class Data {
|
public class Data {
|
||||||
|
|
||||||
@SerializedName(value = "jump_id", alternate = "id")
|
@SerializedName("jump_id")
|
||||||
private String jumpId;
|
private String jumpId;
|
||||||
|
@SerializedName("id")
|
||||||
|
private String id;
|
||||||
@SerializedName(value = "thumbnail", alternate = "path")
|
@SerializedName(value = "thumbnail", alternate = "path")
|
||||||
private String thumbnail;
|
private String thumbnail;
|
||||||
@SerializedName("title")
|
@SerializedName("title")
|
||||||
|
|
@ -24,24 +27,30 @@ public class Data {
|
||||||
@SerializedName("playlist")
|
@SerializedName("playlist")
|
||||||
private Value playlist;
|
private Value playlist;
|
||||||
@SerializedName("year")
|
@SerializedName("year")
|
||||||
private Value year;
|
private String year;
|
||||||
@SerializedName("area")
|
@SerializedName("area")
|
||||||
private Value area;
|
private String area;
|
||||||
@SerializedName("types")
|
@SerializedName("types")
|
||||||
private List<Value> types;
|
private List<Value> types;
|
||||||
@SerializedName("actors")
|
@SerializedName("actors")
|
||||||
private List<Value> actors;
|
private List<Value> actors;
|
||||||
@SerializedName("directors")
|
@SerializedName("directors")
|
||||||
private List<Value> directors;
|
private List<Value> directors;
|
||||||
@SerializedName("btbo_downlist")
|
@SerializedName("source_list_source")
|
||||||
private List<BtboDown> btboDownlist;
|
private List<SourceListSource> source;
|
||||||
|
@SerializedName("dataList")
|
||||||
|
private List<Data> dataList;
|
||||||
|
|
||||||
public String getJumpId() {
|
public String getJumpId() {
|
||||||
return TextUtils.isEmpty(jumpId) ? "" : jumpId;
|
return TextUtils.isEmpty(jumpId) ? "" : jumpId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getThumbnail() {
|
public String getId() {
|
||||||
return TextUtils.isEmpty(thumbnail) ? "" : thumbnail + "@Referer=www.jianpianapp.com@User-Agent=jianpian-version362";
|
return TextUtils.isEmpty(id) ? "" : id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getThumbnail(String imgDomain) {
|
||||||
|
return TextUtils.isEmpty(thumbnail) ? "" : "http://" + imgDomain + thumbnail;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getTitle() {
|
public String getTitle() {
|
||||||
|
|
@ -61,11 +70,11 @@ public class Data {
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getYear() {
|
public String getYear() {
|
||||||
return year == null ? "" : year.getTitle();
|
return year == null ? "" : year;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getArea() {
|
public String getArea() {
|
||||||
return area == null ? "" : area.getTitle();
|
return area == null ? "" : area;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getTypes() {
|
public String getTypes() {
|
||||||
|
|
@ -80,12 +89,20 @@ public class Data {
|
||||||
return directors == null ? "" : getValues(directors, true);
|
return directors == null ? "" : getValues(directors, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<BtboDown> getBtboDownlist() {
|
public List<SourceListSource> getSource() {
|
||||||
return btboDownlist == null ? Collections.emptyList() : btboDownlist;
|
return source == null ? Collections.emptyList() : source;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Vod vod() {
|
public List<Data> getDataList() {
|
||||||
return new Vod(getJumpId(), getTitle(), getThumbnail(), getMask());
|
return dataList == null ? Collections.emptyList() : dataList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vod homeVod(String imgDomain) {
|
||||||
|
return new Vod(getJumpId(), getTitle(), getThumbnail(imgDomain));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vod vod(String imgDomain) {
|
||||||
|
return new Vod(getId(), getTitle(), getThumbnail(imgDomain), getMask());
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getValues(List<Value> items, boolean link) {
|
public String getValues(List<Value> items, boolean link) {
|
||||||
|
|
@ -94,12 +111,6 @@ public class Data {
|
||||||
return Util.substring(sb.toString());
|
return Util.substring(sb.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getPlayUrl() {
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
for (BtboDown value : getBtboDownlist()) sb.append(value.getVal()).append("#");
|
|
||||||
return Util.substring(sb.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class Value {
|
public static class Value {
|
||||||
|
|
||||||
@SerializedName(value = "title", alternate = "name")
|
@SerializedName(value = "title", alternate = "name")
|
||||||
|
|
@ -118,13 +129,51 @@ public class Data {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class BtboDown {
|
public static class SourceListSource {
|
||||||
|
|
||||||
@SerializedName("val")
|
@SerializedName("name")
|
||||||
private String val;
|
private String name;
|
||||||
|
@SerializedName("source_list")
|
||||||
|
private List<SourceList> list;
|
||||||
|
|
||||||
public String getVal() {
|
public String getName() {
|
||||||
return TextUtils.isEmpty(val) ? "" : val.replaceAll("ftp", "tvbox-xg:ftp");
|
return TextUtils.isEmpty(name) ? "" : name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<SourceList> getList() {
|
||||||
|
return list == null ? Collections.emptyList() : list;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class SourceList {
|
||||||
|
|
||||||
|
@SerializedName("source_name")
|
||||||
|
private String name;
|
||||||
|
@SerializedName("url")
|
||||||
|
private String url;
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return TextUtils.isEmpty(name) ? "" : name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUrl() {
|
||||||
|
return TextUtils.isEmpty(url) ? "" : url.replaceAll("ftp", "tvbox-xg:ftp");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getVodFrom() {
|
||||||
|
List<String> items = new ArrayList<>();
|
||||||
|
for (SourceListSource source : getSource()) items.add(source.getName());
|
||||||
|
return TextUtils.join("$$$", items);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getVodUrl() {
|
||||||
|
List<String> items = new ArrayList<>();
|
||||||
|
for (SourceListSource source : getSource()) {
|
||||||
|
List<String> urls = new ArrayList<>();
|
||||||
|
for (SourceList item : source.getList()) urls.add(item.getName() + "$" + item.getUrl());
|
||||||
|
items.add(TextUtils.join("#", urls));
|
||||||
|
}
|
||||||
|
return TextUtils.join("$$$", items);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
package com.github.catvod.bean.jianpian;
|
||||||
|
|
||||||
|
import android.text.TextUtils;
|
||||||
|
|
||||||
|
import com.github.catvod.bean.Vod;
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class Search {
|
||||||
|
|
||||||
|
@SerializedName("data")
|
||||||
|
private List<Search> data;
|
||||||
|
@SerializedName("id")
|
||||||
|
private String id;
|
||||||
|
@SerializedName(value = "thumbnail", alternate = "path")
|
||||||
|
private String thumbnail;
|
||||||
|
@SerializedName("title")
|
||||||
|
private String title;
|
||||||
|
@SerializedName("mask")
|
||||||
|
private String mask;
|
||||||
|
|
||||||
|
public static Search objectFrom(String str) {
|
||||||
|
return new Gson().fromJson(str, Search.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return TextUtils.isEmpty(id) ? "" : id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getThumbnail(String imgDomain) {
|
||||||
|
return TextUtils.isEmpty(thumbnail) ? "" : "http://" + imgDomain + thumbnail;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTitle() {
|
||||||
|
return TextUtils.isEmpty(title) ? "" : title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMask() {
|
||||||
|
return TextUtils.isEmpty(mask) ? "" : mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vod vod(String imgDomain) {
|
||||||
|
return new Vod(getId(), getTitle(), getThumbnail(imgDomain), getMask());
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Search> getData() {
|
||||||
|
return data == null ? Collections.emptyList() : data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
package com.github.catvod.bean.pan123;
|
||||||
|
|
||||||
|
import com.github.catvod.api.Pan123Handler;
|
||||||
|
import com.github.catvod.crawler.SpiderDebug;
|
||||||
|
import com.github.catvod.spider.Init;
|
||||||
|
import com.github.catvod.utils.Path;
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
public class Cache {
|
||||||
|
|
||||||
|
@SerializedName("user")
|
||||||
|
private User user;
|
||||||
|
|
||||||
|
|
||||||
|
public static Cache objectFrom(String str) {
|
||||||
|
if(str.isEmpty()) return new Cache();
|
||||||
|
SpiderDebug.log("Cache.objectFrom: " + str);
|
||||||
|
Cache item = new Gson().fromJson(str, Cache.class);
|
||||||
|
return item == null ? new Cache() : item;
|
||||||
|
}
|
||||||
|
|
||||||
|
public User getUser() {
|
||||||
|
return user == null ? new User() : user;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void setUserInfo(User user) {
|
||||||
|
this.user = user;
|
||||||
|
this.saveUser();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void saveUser() {
|
||||||
|
SpiderDebug.log("Cache.saveUser: " + toString());
|
||||||
|
SpiderDebug.log("Cache.path: " + Pan123Handler.INSTANCE.getCache().getAbsolutePath());
|
||||||
|
Init.execute(() -> Path.write(Pan123Handler.INSTANCE.getCache(), toString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return new Gson().toJson(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
package com.github.catvod.bean.pan123
|
||||||
|
|
||||||
|
data class ShareInfo(
|
||||||
|
val filename: String,
|
||||||
|
val shareKey: String,
|
||||||
|
val sharePwd: String,
|
||||||
|
val next: Int,
|
||||||
|
val fileId: Long,
|
||||||
|
val S3KeyFlag: String,
|
||||||
|
val Size: Long,
|
||||||
|
val Etag: String
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
package com.github.catvod.bean.pan123;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
public class User {
|
||||||
|
|
||||||
|
@SerializedName("expire")
|
||||||
|
private long expire;
|
||||||
|
@SerializedName("cookie")
|
||||||
|
private String cookie;
|
||||||
|
|
||||||
|
@SerializedName("userName")
|
||||||
|
private String userName;
|
||||||
|
@SerializedName("password")
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
public String getUserName() {
|
||||||
|
return userName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUserName(String userName) {
|
||||||
|
this.userName = userName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPassword() {
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPassword(String password) {
|
||||||
|
this.password = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCookie() {
|
||||||
|
return cookie;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCookie(String cookie) {
|
||||||
|
this.cookie = cookie;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getExpire() {
|
||||||
|
return expire;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setExpire(long expire) {
|
||||||
|
this.expire = expire;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static User objectFrom(String str) {
|
||||||
|
User item = new Gson().fromJson(str, User.class);
|
||||||
|
return item == null ? new User() : item;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void clean() {
|
||||||
|
this.cookie = "";
|
||||||
|
this.userName = "";
|
||||||
|
this.password = "";
|
||||||
|
this.expire = 0L;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
package com.github.catvod.bean.quark;
|
||||||
|
|
||||||
|
import com.github.catvod.api.QuarkApi;
|
||||||
|
import com.github.catvod.spider.Init;
|
||||||
|
import com.github.catvod.utils.Path;
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
public class Cache {
|
||||||
|
|
||||||
|
@SerializedName("user")
|
||||||
|
private User user;
|
||||||
|
|
||||||
|
|
||||||
|
public static Cache objectFrom(String str) {
|
||||||
|
Cache item = new Gson().fromJson(str, Cache.class);
|
||||||
|
return item == null ? new Cache() : item;
|
||||||
|
}
|
||||||
|
|
||||||
|
public User getUser() {
|
||||||
|
return user == null ? new User("") : user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUser(User user) {
|
||||||
|
this.user = user;
|
||||||
|
this.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void save() {
|
||||||
|
Init.execute(() -> Path.write(QuarkApi.get().getCache(), toString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return new Gson().toJson(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,112 @@
|
||||||
|
package com.github.catvod.bean.quark;
|
||||||
|
|
||||||
|
import com.github.catvod.utils.Util;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
public class Item {
|
||||||
|
private String fileId;
|
||||||
|
private String shareId;
|
||||||
|
private String shareToken;
|
||||||
|
private String shareFileToken;
|
||||||
|
private String seriesId;
|
||||||
|
private String name;
|
||||||
|
private String type;
|
||||||
|
private String formatType;
|
||||||
|
private Double size;
|
||||||
|
private String parent;
|
||||||
|
private String shareData;
|
||||||
|
private int shareIndex;
|
||||||
|
private Double lastUpdateAt;
|
||||||
|
private String subtitle;
|
||||||
|
|
||||||
|
public Item() {
|
||||||
|
this.fileId = "";
|
||||||
|
this.shareId = "";
|
||||||
|
this.shareToken = "";
|
||||||
|
this.shareFileToken = "";
|
||||||
|
this.seriesId = "";
|
||||||
|
this.name = "";
|
||||||
|
this.type = "";
|
||||||
|
this.formatType = "";
|
||||||
|
this.size = 0d;
|
||||||
|
this.parent = "";
|
||||||
|
this.shareData = null;
|
||||||
|
this.shareIndex = 0;
|
||||||
|
this.lastUpdateAt = 0d;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Item objectFrom(Map<String, Object> item_json, String shareId, int shareIndex) {
|
||||||
|
Item item = new Item();
|
||||||
|
item.fileId = item_json.get("fid") != null ? (String) item_json.get("fid") : "";
|
||||||
|
item.shareId = shareId;
|
||||||
|
item.shareToken = item_json.get("stoken") != null ? (String) item_json.get("stoken") : "";
|
||||||
|
item.shareFileToken = item_json.get("share_fid_token") != null ? (String) item_json.get("share_fid_token") : "";
|
||||||
|
item.seriesId = item_json.get("series_id") != null ? (String) item_json.get("series_id") : "";
|
||||||
|
item.name = item_json.get("file_name") != null ? (String) item_json.get("file_name") : "";
|
||||||
|
item.type = item_json.get("obj_category") != null ? (String) item_json.get("obj_category") : "";
|
||||||
|
item.formatType = item_json.get("format_type") != null ? (String) item_json.get("format_type") : "";
|
||||||
|
item.size = item_json.get("size") != null ? (Double) item_json.get("size") : 0d;
|
||||||
|
item.parent = item_json.get("pdir_fid") != null ? (String) item_json.get("pdir_fid") : "";
|
||||||
|
item.lastUpdateAt = item_json.get("last_update_at") != null ? (Double) item_json.get("last_update_at") : Double.valueOf(0d);
|
||||||
|
item.shareIndex = shareIndex;
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFileExtension() {
|
||||||
|
String[] arr = name.split("\\.");
|
||||||
|
return arr[arr.length - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSubtitle() {
|
||||||
|
return subtitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSubtitle(String subtitle) {
|
||||||
|
this.subtitle = subtitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFileId() {
|
||||||
|
return fileId.isEmpty() ? "" : fileId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name.isEmpty() ? "" : name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getParent() {
|
||||||
|
return parent.isEmpty() ? "" : "[" + parent + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSize() {
|
||||||
|
return size.equals("0") ? "" : "[" + size + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public int getShareIndex() {
|
||||||
|
return shareIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDisplayName(String type_name) {
|
||||||
|
String name = getName();
|
||||||
|
if (type_name.equals("电视剧")) {
|
||||||
|
String[] replaceNameList = {"4k", "4K"};
|
||||||
|
name = name.replaceAll("\\." + getFileExtension(), "");
|
||||||
|
for (String replaceName : replaceNameList) {
|
||||||
|
name = name.replaceAll(replaceName, "");
|
||||||
|
}
|
||||||
|
name = Pattern.compile("/\\.S01E(.*?)\\./").matcher(name).find() ? name.split("/\\.S01E(.*?)\\./")[1] : name;
|
||||||
|
String[] numbers = name.split("\\d+");
|
||||||
|
if (numbers.length > 0) {
|
||||||
|
name = numbers[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return name + " " + Util.getSize(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEpisodeUrl(String type_name) {
|
||||||
|
return getDisplayName(type_name) + "$" + getFileId() + "++" + shareFileToken + "++" + shareId + "++" + shareToken;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
package com.github.catvod.bean.quark;
|
||||||
|
|
||||||
|
public class ShareData {
|
||||||
|
private String shareId;
|
||||||
|
private String folderId;
|
||||||
|
private String sharePwd ;
|
||||||
|
|
||||||
|
public ShareData(String shareId, String folderId) {
|
||||||
|
this.shareId = shareId;
|
||||||
|
this.folderId = folderId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSharePwd() {
|
||||||
|
return sharePwd;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSharePwd(String sharePwd) {
|
||||||
|
this.sharePwd = sharePwd;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getShareId() {
|
||||||
|
return shareId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setShareId(String shareId) {
|
||||||
|
this.shareId = shareId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFolderId() {
|
||||||
|
return folderId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFolderId(String folderId) {
|
||||||
|
this.folderId = folderId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
package com.github.catvod.bean.quark;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
public class User {
|
||||||
|
public User(String cookie) {
|
||||||
|
this.cookie = cookie;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SerializedName("cookie")
|
||||||
|
private String cookie;
|
||||||
|
|
||||||
|
public String getCookie() {
|
||||||
|
return cookie;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCookie(String cookie) {
|
||||||
|
this.cookie = cookie;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static User objectFrom(String cookie) {
|
||||||
|
return new User(cookie);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void clean() {
|
||||||
|
this.cookie = "";
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
package com.github.catvod.bean.tianyi;
|
||||||
|
|
||||||
|
import com.github.catvod.api.TianYiHandler;
|
||||||
|
import com.github.catvod.crawler.SpiderDebug;
|
||||||
|
import com.github.catvod.spider.Init;
|
||||||
|
import com.github.catvod.utils.Path;
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
public class Cache {
|
||||||
|
|
||||||
|
@SerializedName("user")
|
||||||
|
private User user;
|
||||||
|
|
||||||
|
|
||||||
|
public static Cache objectFrom(String str) {
|
||||||
|
SpiderDebug.log("Cache.objectFrom: " + str);
|
||||||
|
Cache item = new Gson().fromJson(str, Cache.class);
|
||||||
|
return item == null ? new Cache() : item;
|
||||||
|
}
|
||||||
|
|
||||||
|
public User getUser() {
|
||||||
|
return user == null ? new User("") : user;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void setTianyiUser(User user) {
|
||||||
|
this.user = user;
|
||||||
|
this.saveTianyiUser();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void saveTianyiUser() {
|
||||||
|
Init.execute(() -> Path.write( TianYiHandler.get().getCache(), toString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void saveTianyieUser() {
|
||||||
|
Init.execute(() -> Path.write( TianYiHandler.get().geteCache(), toString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return new Gson().toJson(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void setTianyieUser(User user) {
|
||||||
|
this.user = user;
|
||||||
|
this.saveTianyieUser();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,115 @@
|
||||||
|
package com.github.catvod.bean.tianyi;
|
||||||
|
|
||||||
|
import com.github.catvod.utils.Util;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
public class Item {
|
||||||
|
private String fileId;
|
||||||
|
private String shareId;
|
||||||
|
private String shareToken;
|
||||||
|
private String shareFileToken;
|
||||||
|
private String seriesId;
|
||||||
|
private String name;
|
||||||
|
private String type;
|
||||||
|
private String formatType;
|
||||||
|
private Double size;
|
||||||
|
private String parent;
|
||||||
|
private String shareData;
|
||||||
|
private int shareIndex;
|
||||||
|
private Double lastUpdateAt;
|
||||||
|
private String subtitle;
|
||||||
|
|
||||||
|
public Item() {
|
||||||
|
this.fileId = "";
|
||||||
|
this.shareId = "";
|
||||||
|
this.shareToken = "";
|
||||||
|
this.shareFileToken = "";
|
||||||
|
this.seriesId = "";
|
||||||
|
this.name = "";
|
||||||
|
this.type = "";
|
||||||
|
this.formatType = "";
|
||||||
|
this.size = 0d;
|
||||||
|
this.parent = "";
|
||||||
|
this.shareData = null;
|
||||||
|
this.shareIndex = 0;
|
||||||
|
this.lastUpdateAt = 0d;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Item objectFrom(JsonObject item_json, String shareId, int shareIndex) {
|
||||||
|
Item item = new Item();
|
||||||
|
item.fileId = item_json.get("id") != null ? item_json.get("id").getAsString() : "";
|
||||||
|
item.shareId = shareId;
|
||||||
|
item.name = item_json.get("name") != null ? item_json.get("name").getAsString() : "";
|
||||||
|
item.size = item_json.get("size") != null ? item_json.get("size").getAsDouble() : 0d;
|
||||||
|
/* item.shareToken = item_json.get("stoken") != null ? (String) item_json.get("stoken") : "";
|
||||||
|
item.shareFileToken = item_json.get("share_fid_token") != null ? (String) item_json.get("share_fid_token") : "";
|
||||||
|
item.seriesId = item_json.get("series_id") != null ? (String) item_json.get("series_id") : "";
|
||||||
|
|
||||||
|
item.type = item_json.get("obj_category") != null ? (String) item_json.get("obj_category") : "";
|
||||||
|
item.formatType = item_json.get("format_type") != null ? (String) item_json.get("format_type") : "";
|
||||||
|
item.size = item_json.get("size") != null ? (Double) item_json.get("size") : 0d;
|
||||||
|
item.parent = item_json.get("pdir_fid") != null ? (String) item_json.get("pdir_fid") : "";
|
||||||
|
item.lastUpdateAt = item_json.get("last_update_at") != null ? (Double) item_json.get("last_update_at") : Double.valueOf(0d);
|
||||||
|
item.shareIndex = shareIndex;*/
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFileExtension() {
|
||||||
|
String[] arr = name.split("\\.");
|
||||||
|
return arr[arr.length - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSubtitle() {
|
||||||
|
return subtitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSubtitle(String subtitle) {
|
||||||
|
this.subtitle = subtitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFileId() {
|
||||||
|
return fileId.isEmpty() ? "" : fileId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name.isEmpty() ? "" : name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getParent() {
|
||||||
|
return parent.isEmpty() ? "" : "[" + parent + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSize() {
|
||||||
|
return size.equals("0") ? "" : "[" + size + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public int getShareIndex() {
|
||||||
|
return shareIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDisplayName(String type_name) {
|
||||||
|
String name = getName();
|
||||||
|
if (type_name.equals("电视剧")) {
|
||||||
|
String[] replaceNameList = {"4k", "4K"};
|
||||||
|
name = name.replaceAll("\\." + getFileExtension(), "");
|
||||||
|
for (String replaceName : replaceNameList) {
|
||||||
|
name = name.replaceAll(replaceName, "");
|
||||||
|
}
|
||||||
|
name = Pattern.compile("/\\.S01E(.*?)\\./").matcher(name).find() ? name.split("/\\.S01E(.*?)\\./")[1] : name;
|
||||||
|
String[] numbers = name.split("\\d+");
|
||||||
|
if (numbers.length > 0) {
|
||||||
|
name = numbers[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return name + " " + Util.getSize(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEpisodeUrl(String type_name) {
|
||||||
|
return getDisplayName(type_name) + "$" + getFileId() + "++" + shareId ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,65 @@
|
||||||
|
package com.github.catvod.bean.tianyi;
|
||||||
|
|
||||||
|
public class ShareData {
|
||||||
|
|
||||||
|
private String shareId;
|
||||||
|
private String folderId;
|
||||||
|
private String sharePwd;
|
||||||
|
private String fileId;
|
||||||
|
private Integer shareMode;
|
||||||
|
private Boolean isFolder;
|
||||||
|
|
||||||
|
|
||||||
|
public String getFileId() {
|
||||||
|
return fileId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFileId(String fileId) {
|
||||||
|
this.fileId = fileId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getShareMode() {
|
||||||
|
return shareMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setShareMode(Integer shareMode) {
|
||||||
|
this.shareMode = shareMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean getFolder() {
|
||||||
|
return isFolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFolder(Boolean folder) {
|
||||||
|
isFolder = folder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ShareData(String shareId, String folderId) {
|
||||||
|
this.shareId = shareId;
|
||||||
|
this.folderId = folderId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSharePwd() {
|
||||||
|
return sharePwd;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSharePwd(String sharePwd) {
|
||||||
|
this.sharePwd = sharePwd;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getShareId() {
|
||||||
|
return shareId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setShareId(String shareId) {
|
||||||
|
this.shareId = shareId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFolderId() {
|
||||||
|
return folderId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFolderId(String folderId) {
|
||||||
|
this.folderId = folderId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
package com.github.catvod.bean.tianyi;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
public class User {
|
||||||
|
public User(String cookie) {
|
||||||
|
this.cookie = cookie;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SerializedName("cookie")
|
||||||
|
private String cookie;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public String getCookie() {
|
||||||
|
return cookie;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCookie(String cookie) {
|
||||||
|
this.cookie = cookie;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static User objectFrom(String cookie) {
|
||||||
|
return new User(cookie);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void clean() {
|
||||||
|
this.cookie = "";
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
package com.github.catvod.bean.uc;
|
||||||
|
|
||||||
|
import com.github.catvod.api.TianYiHandler;
|
||||||
|
import com.github.catvod.api.UCApi;
|
||||||
|
import com.github.catvod.api.UCTokenHandler;
|
||||||
|
import com.github.catvod.crawler.SpiderDebug;
|
||||||
|
import com.github.catvod.spider.Init;
|
||||||
|
import com.github.catvod.utils.Path;
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
public class Cache {
|
||||||
|
|
||||||
|
@SerializedName("user")
|
||||||
|
private User user;
|
||||||
|
|
||||||
|
|
||||||
|
public static Cache objectFrom(String str) {
|
||||||
|
SpiderDebug.log("Cache.objectFrom: " + str);
|
||||||
|
Cache item = new Gson().fromJson(str, Cache.class);
|
||||||
|
return item == null ? new Cache() : item;
|
||||||
|
}
|
||||||
|
|
||||||
|
public User getUser() {
|
||||||
|
return user == null ? new User("") : user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUser(User user) {
|
||||||
|
this.user = user;
|
||||||
|
this.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTokenUser(User user) {
|
||||||
|
this.user = user;
|
||||||
|
this.saveToken();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public void saveToken() {
|
||||||
|
Init.execute(() -> Path.write(new UCTokenHandler().getCache(), toString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void save() {
|
||||||
|
Init.execute(() -> Path.write(UCApi.get().getCache(), toString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return new Gson().toJson(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,112 @@
|
||||||
|
package com.github.catvod.bean.uc;
|
||||||
|
|
||||||
|
import com.github.catvod.utils.Util;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
public class Item {
|
||||||
|
private String fileId;
|
||||||
|
private String shareId;
|
||||||
|
private String shareToken;
|
||||||
|
private String shareFileToken;
|
||||||
|
private String seriesId;
|
||||||
|
private String name;
|
||||||
|
private String type;
|
||||||
|
private String formatType;
|
||||||
|
private Double size;
|
||||||
|
private String parent;
|
||||||
|
private String shareData;
|
||||||
|
private int shareIndex;
|
||||||
|
private Double lastUpdateAt;
|
||||||
|
private String subtitle;
|
||||||
|
|
||||||
|
public Item() {
|
||||||
|
this.fileId = "";
|
||||||
|
this.shareId = "";
|
||||||
|
this.shareToken = "";
|
||||||
|
this.shareFileToken = "";
|
||||||
|
this.seriesId = "";
|
||||||
|
this.name = "";
|
||||||
|
this.type = "";
|
||||||
|
this.formatType = "";
|
||||||
|
this.size = 0d;
|
||||||
|
this.parent = "";
|
||||||
|
this.shareData = null;
|
||||||
|
this.shareIndex = 0;
|
||||||
|
this.lastUpdateAt = 0d;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Item objectFrom(Map<String, Object> item_json, String shareId, int shareIndex) {
|
||||||
|
Item item = new Item();
|
||||||
|
item.fileId = item_json.get("fid") != null ? (String) item_json.get("fid") : "";
|
||||||
|
item.shareId = shareId;
|
||||||
|
item.shareToken = item_json.get("stoken") != null ? (String) item_json.get("stoken") : "";
|
||||||
|
item.shareFileToken = item_json.get("share_fid_token") != null ? (String) item_json.get("share_fid_token") : "";
|
||||||
|
item.seriesId = item_json.get("series_id") != null ? (String) item_json.get("series_id") : "";
|
||||||
|
item.name = item_json.get("file_name") != null ? (String) item_json.get("file_name") : "";
|
||||||
|
item.type = item_json.get("obj_category") != null ? (String) item_json.get("obj_category") : "";
|
||||||
|
item.formatType = item_json.get("format_type") != null ? (String) item_json.get("format_type") : "";
|
||||||
|
item.size = item_json.get("size") != null ? (Double) item_json.get("size") : 0d;
|
||||||
|
item.parent = item_json.get("pdir_fid") != null ? (String) item_json.get("pdir_fid") : "";
|
||||||
|
item.lastUpdateAt = item_json.get("last_update_at") != null ? (Double) item_json.get("last_update_at") : Double.valueOf(0d);
|
||||||
|
item.shareIndex = shareIndex;
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFileExtension() {
|
||||||
|
String[] arr = name.split("\\.");
|
||||||
|
return arr[arr.length - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSubtitle() {
|
||||||
|
return subtitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSubtitle(String subtitle) {
|
||||||
|
this.subtitle = subtitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFileId() {
|
||||||
|
return fileId.isEmpty() ? "" : fileId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name.isEmpty() ? "" : name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getParent() {
|
||||||
|
return parent.isEmpty() ? "" : "[" + parent + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSize() {
|
||||||
|
return size.equals("0") ? "" : "[" + size + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public int getShareIndex() {
|
||||||
|
return shareIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDisplayName(String type_name) {
|
||||||
|
String name = getName();
|
||||||
|
if (type_name.equals("电视剧")) {
|
||||||
|
String[] replaceNameList = {"4k", "4K"};
|
||||||
|
name = name.replaceAll("\\." + getFileExtension(), "");
|
||||||
|
for (String replaceName : replaceNameList) {
|
||||||
|
name = name.replaceAll(replaceName, "");
|
||||||
|
}
|
||||||
|
name = Pattern.compile("/\\.S01E(.*?)\\./").matcher(name).find() ? name.split("/\\.S01E(.*?)\\./")[1] : name;
|
||||||
|
String[] numbers = name.split("\\d+");
|
||||||
|
if (numbers.length > 0) {
|
||||||
|
name = numbers[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return name + " " + Util.getSize(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEpisodeUrl(String type_name) {
|
||||||
|
return getDisplayName(type_name) + "$" + getFileId() + "++" + shareFileToken + "++" + shareId + "++" + shareToken;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
package com.github.catvod.bean.uc;
|
||||||
|
|
||||||
|
public class ShareData {
|
||||||
|
private String shareId;
|
||||||
|
private String folderId;
|
||||||
|
private String sharePwd ;
|
||||||
|
|
||||||
|
public ShareData(String shareId, String folderId) {
|
||||||
|
this.shareId = shareId;
|
||||||
|
this.folderId = folderId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSharePwd() {
|
||||||
|
return sharePwd;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSharePwd(String sharePwd) {
|
||||||
|
this.sharePwd = sharePwd;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getShareId() {
|
||||||
|
return shareId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setShareId(String shareId) {
|
||||||
|
this.shareId = shareId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFolderId() {
|
||||||
|
return folderId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFolderId(String folderId) {
|
||||||
|
this.folderId = folderId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
package com.github.catvod.bean.uc;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
public class User {
|
||||||
|
public User(String cookie) {
|
||||||
|
this.cookie = cookie;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SerializedName("cookie")
|
||||||
|
private String cookie;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public String getCookie() {
|
||||||
|
return cookie;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCookie(String cookie) {
|
||||||
|
this.cookie = cookie;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static User objectFrom(String cookie) {
|
||||||
|
return new User(cookie);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void clean() {
|
||||||
|
this.cookie = "";
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
package com.github.catvod.bean.yun;
|
||||||
|
|
||||||
|
|
||||||
|
import com.github.catvod.api.YunTokenHandler;
|
||||||
|
import com.github.catvod.spider.Init;
|
||||||
|
import com.github.catvod.utils.Path;
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
public class Cache {
|
||||||
|
|
||||||
|
@SerializedName("user")
|
||||||
|
private User user;
|
||||||
|
|
||||||
|
|
||||||
|
public static Cache objectFrom(String str) {
|
||||||
|
Cache item = new Gson().fromJson(str, Cache.class);
|
||||||
|
return item == null ? new Cache() : item;
|
||||||
|
}
|
||||||
|
|
||||||
|
public User getUser() {
|
||||||
|
return user == null ? new User("") : user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUser(User user) {
|
||||||
|
this.user = user;
|
||||||
|
this.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void save() {
|
||||||
|
Init.execute(() -> Path.write(YunTokenHandler.get().getCache(), toString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return new Gson().toJson(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
package com.github.catvod.bean.yun;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
public class User {
|
||||||
|
public User(String cookie) {
|
||||||
|
this.cookie = cookie;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SerializedName("cookie")
|
||||||
|
private String cookie;
|
||||||
|
|
||||||
|
public String getCookie() {
|
||||||
|
return cookie;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCookie(String cookie) {
|
||||||
|
this.cookie = cookie;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static User objectFrom(String cookie) {
|
||||||
|
return new User(cookie);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void clean() {
|
||||||
|
this.cookie = "";
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -11,6 +11,7 @@ public class SpiderDebug {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void log(String msg) {
|
public static void log(String msg) {
|
||||||
|
System.out.println(msg);
|
||||||
Log.d(TAG, msg);
|
Log.d(TAG, msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,13 @@ import android.widget.Button;
|
||||||
|
|
||||||
import com.github.catvod.R;
|
import com.github.catvod.R;
|
||||||
import com.github.catvod.crawler.Spider;
|
import com.github.catvod.crawler.Spider;
|
||||||
import com.github.catvod.spider.Init;
|
import com.github.catvod.net.OkHttp;
|
||||||
import com.github.catvod.spider.PTT;
|
import com.github.catvod.spider.*;
|
||||||
|
import com.github.catvod.utils.Util;
|
||||||
import com.orhanobut.logger.AndroidLogAdapter;
|
import com.orhanobut.logger.AndroidLogAdapter;
|
||||||
import com.orhanobut.logger.Logger;
|
import com.orhanobut.logger.Logger;
|
||||||
|
import com.whl.quickjs.android.QuickJSLoader;
|
||||||
|
import com.whl.quickjs.wrapper.QuickJSContext;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
@ -22,9 +25,11 @@ public class MainActivity extends Activity {
|
||||||
private ExecutorService executor;
|
private ExecutorService executor;
|
||||||
private Spider spider;
|
private Spider spider;
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
setContentView(R.layout.activity_main);
|
setContentView(R.layout.activity_main);
|
||||||
Button homeContent = findViewById(R.id.homeContent);
|
Button homeContent = findViewById(R.id.homeContent);
|
||||||
Button homeVideoContent = findViewById(R.id.homeVideoContent);
|
Button homeVideoContent = findViewById(R.id.homeVideoContent);
|
||||||
|
|
@ -41,12 +46,15 @@ public class MainActivity extends Activity {
|
||||||
Logger.addLogAdapter(new AndroidLogAdapter());
|
Logger.addLogAdapter(new AndroidLogAdapter());
|
||||||
executor = Executors.newCachedThreadPool();
|
executor = Executors.newCachedThreadPool();
|
||||||
executor.execute(this::initSpider);
|
executor.execute(this::initSpider);
|
||||||
|
//String content = OkHttp.string("https://androidcatvodspider.netlify.app/json/js/newvision.js");
|
||||||
|
// byte[] bytes = context.compileModule(content, "newvision.js");
|
||||||
|
// String result = "//bb" + Util.base64Encode(bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initSpider() {
|
private void initSpider() {
|
||||||
try {
|
try {
|
||||||
Init.init(getApplicationContext());
|
Init.init(getApplicationContext());
|
||||||
spider = new PTT();
|
spider = new Wogg();
|
||||||
spider.init(this, "");
|
spider.init(this, "");
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
|
@ -74,7 +82,7 @@ public class MainActivity extends Activity {
|
||||||
HashMap<String, String> extend = new HashMap<>();
|
HashMap<String, String> extend = new HashMap<>();
|
||||||
extend.put("c", "19");
|
extend.put("c", "19");
|
||||||
extend.put("year", "2024");
|
extend.put("year", "2024");
|
||||||
Logger.t("categoryContent").d(spider.categoryContent("3", "2", true, extend));
|
Logger.t("categoryContent").d(spider.categoryContent("1", "2", true, extend));
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
@ -82,7 +90,8 @@ public class MainActivity extends Activity {
|
||||||
|
|
||||||
public void detailContent() {
|
public void detailContent() {
|
||||||
try {
|
try {
|
||||||
Logger.t("detailContent").d(spider.detailContent(Arrays.asList("434686")));
|
// Logger.t("detailContent").d(spider.detailContent(Arrays.asList("https://drive.uc.cn/s/1bc52309b62f4?public=1")));
|
||||||
|
Logger.t("detailContent").d(spider.detailContent(Arrays.asList("/voddetail/88220.html")));
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
@ -90,7 +99,7 @@ public class MainActivity extends Activity {
|
||||||
|
|
||||||
public void playerContent() {
|
public void playerContent() {
|
||||||
try {
|
try {
|
||||||
Logger.t("playerContent").d(spider.playerContent("", "382044/1/78", new ArrayList<>()));
|
Logger.t("playerContent").d(spider.playerContent("Uc4K", "c346b2883f0346d793b2aab7dbf6c08c++6924b088a6f624fd5baa769bf48ae337++1bc52309b62f4++YmXbBVC2f9W1Frq2ji2l0p0TxSaJhSr7HjrKRat1E6c=", new ArrayList<>()));
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ public class OkHttp {
|
||||||
|
|
||||||
private OkHttpClient client;
|
private OkHttpClient client;
|
||||||
|
|
||||||
|
|
||||||
private static class Loader {
|
private static class Loader {
|
||||||
static volatile OkHttp INSTANCE = new OkHttp();
|
static volatile OkHttp INSTANCE = new OkHttp();
|
||||||
}
|
}
|
||||||
|
|
@ -53,6 +54,10 @@ public class OkHttp {
|
||||||
return url.startsWith("http") ? new OkRequest(GET, url, params, header).execute(client()).getBody() : "";
|
return url.startsWith("http") ? new OkRequest(GET, url, params, header).execute(client()).getBody() : "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static OkResult get(String url, Map<String, String> params, Map<String, String> header) {
|
||||||
|
return new OkRequest(GET, url, params, header).execute(client());
|
||||||
|
}
|
||||||
|
|
||||||
public static String post(String url, Map<String, String> params) {
|
public static String post(String url, Map<String, String> params) {
|
||||||
return post(url, params, null).getBody();
|
return post(url, params, null).getBody();
|
||||||
}
|
}
|
||||||
|
|
@ -72,6 +77,9 @@ public class OkHttp {
|
||||||
public static String getLocation(String url, Map<String, String> header) throws IOException {
|
public static String getLocation(String url, Map<String, String> header) throws IOException {
|
||||||
return getLocation(client().newBuilder().followRedirects(false).followSslRedirects(false).build().newCall(new Request.Builder().url(url).headers(Headers.of(header)).build()).execute().headers().toMultimap());
|
return getLocation(client().newBuilder().followRedirects(false).followSslRedirects(false).build().newCall(new Request.Builder().url(url).headers(Headers.of(header)).build()).execute().headers().toMultimap());
|
||||||
}
|
}
|
||||||
|
public static Map<String, List<String>> getLocationHeader(String url, Map<String, String> header) throws IOException {
|
||||||
|
return client().newBuilder().followRedirects(false).followSslRedirects(false).build().newCall(new Request.Builder().url(url).headers(Headers.of(header)).build()).execute().headers().toMultimap();
|
||||||
|
}
|
||||||
|
|
||||||
public static String getLocation(Map<String, List<String>> headers) {
|
public static String getLocation(Map<String, List<String>> headers) {
|
||||||
if (headers == null) return null;
|
if (headers == null) return null;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,111 @@
|
||||||
|
package com.github.catvod.net;
|
||||||
|
|
||||||
|
import com.github.catvod.crawler.Spider;
|
||||||
|
import okhttp3.*;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
public class OkHttpWithCookie {
|
||||||
|
|
||||||
|
public static final String POST = "POST";
|
||||||
|
public static final String GET = "GET";
|
||||||
|
|
||||||
|
private OkHttpClient client;
|
||||||
|
|
||||||
|
|
||||||
|
private static class Loader {
|
||||||
|
static volatile OkHttpWithCookie INSTANCE = new OkHttpWithCookie();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static OkHttpWithCookie get() {
|
||||||
|
return Loader.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Response newCall(Request request, CookieJar cookieJar) throws IOException {
|
||||||
|
return client(cookieJar).newCall(request).execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Response newCall(String url, CookieJar cookieJar) throws IOException {
|
||||||
|
return client(cookieJar).newCall(new Request.Builder().url(url).build()).execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Response newCall(String url, Map<String, String> header, CookieJar cookieJar) throws IOException {
|
||||||
|
return client(cookieJar).newCall(new Request.Builder().url(url).headers(Headers.of(header)).build()).execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String string(String url, CookieJar cookieJar) {
|
||||||
|
return string(url, null, cookieJar);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String string(String url, Map<String, String> header, CookieJar cookieJar) {
|
||||||
|
return string(url, null, header, cookieJar);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String string(String url, Map<String, String> params, Map<String, String> header, CookieJar cookieJar) {
|
||||||
|
return url.startsWith("http") ? new OkRequest(GET, url, params, header).execute(client(cookieJar)).getBody() : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static OkResult get(String url, Map<String, String> params, Map<String, String> header, CookieJar cookieJar) {
|
||||||
|
return new OkRequest(GET, url, params, header).execute(client(cookieJar));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String post(String url, Map<String, String> params, CookieJar cookieJar) {
|
||||||
|
return post(url, params, null, cookieJar).getBody();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static OkResult post(String url, Map<String, String> params, Map<String, String> header, CookieJar cookieJar) {
|
||||||
|
return new OkRequest(POST, url, params, header).execute(client(cookieJar));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String post(String url, String json, CookieJar cookieJar) {
|
||||||
|
return post(url, json, null, cookieJar).getBody();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static OkResult post(String url, String json, Map<String, String> header, CookieJar cookieJar) {
|
||||||
|
return new OkRequest(POST, url, json, header).execute(client(cookieJar));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getLocation(String url, Map<String, String> header, CookieJar cookieJar) throws IOException {
|
||||||
|
return getLocation(client(cookieJar).newBuilder().followRedirects(false).followSslRedirects(false).build().newCall(new Request.Builder().url(url).headers(Headers.of(header)).build()).execute().headers().toMultimap());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Map<String, List<String>> getLocationHeader(String url, Map<String, String> header, CookieJar cookieJar) throws IOException {
|
||||||
|
return client(cookieJar).newBuilder().followRedirects(false).followSslRedirects(false).build().newCall(new Request.Builder().url(url).headers(Headers.of(header)).build()).execute().headers().toMultimap();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getLocation(Map<String, List<String>> headers) {
|
||||||
|
if (headers == null) return null;
|
||||||
|
if (headers.containsKey("location")) return headers.get("location").get(0);
|
||||||
|
if (headers.containsKey("Location")) return headers.get("Location").get(0);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static OkHttpClient build(CookieJar cookieJar) {
|
||||||
|
if (get().client != null) return get().client;
|
||||||
|
return get().client = getBuilder(cookieJar).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static OkHttpClient.Builder getBuilder(CookieJar cookieJar) {
|
||||||
|
return new OkHttpClient.Builder().cookieJar(cookieJar).dns(safeDns()).connectTimeout(30, TimeUnit.SECONDS).readTimeout(30, TimeUnit.SECONDS).writeTimeout(30, TimeUnit.SECONDS).hostnameVerifier((hostname, session) -> true).sslSocketFactory(new SSLCompat(), SSLCompat.TM);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static OkHttpClient client(CookieJar cookieJar) {
|
||||||
|
try {
|
||||||
|
return Objects.requireNonNull(Spider.client());
|
||||||
|
} catch (Throwable e) {
|
||||||
|
return build(cookieJar);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Dns safeDns() {
|
||||||
|
try {
|
||||||
|
return Objects.requireNonNull(Spider.safeDns());
|
||||||
|
} catch (Throwable e) {
|
||||||
|
return Dns.SYSTEM;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -50,15 +50,16 @@ public class Ali extends Spider {
|
||||||
* 獲取詳情內容視頻播放來源(多 shared_link)
|
* 獲取詳情內容視頻播放來源(多 shared_link)
|
||||||
*
|
*
|
||||||
* @param ids share_link 集合
|
* @param ids share_link 集合
|
||||||
|
* @param i
|
||||||
* @return 詳情內容視頻播放來源
|
* @return 詳情內容視頻播放來源
|
||||||
*/
|
*/
|
||||||
public String detailContentVodPlayFrom(List<String> ids) {
|
public String detailContentVodPlayFrom(List<String> ids, int index) {
|
||||||
List<String> playFrom = new ArrayList<>();
|
List<String> playFrom = new ArrayList<>();
|
||||||
if (ids.size() < 2) return TextUtils.join("$$$", Arrays.asList("轉存原畫", "分享原畫", "代理普畫"));
|
// if (ids.size() < 2) return TextUtils.join("$$$", Arrays.asList("轉存原畫", "分享原畫", "代理普畫"));
|
||||||
for (int i = 1; i <= ids.size(); i++) {
|
for (int i = 1; i <= ids.size(); i++) {
|
||||||
playFrom.add(String.format(Locale.getDefault(), "轉存原畫#%02d", i));
|
playFrom.add(String.format(Locale.getDefault(), "轉存原畫#%02d%02d", i,index));
|
||||||
playFrom.add(String.format(Locale.getDefault(), "分享原畫#%02d", i));
|
playFrom.add(String.format(Locale.getDefault(), "分享原畫#%02d%02d", i,index));
|
||||||
playFrom.add(String.format(Locale.getDefault(), "代理普畫#%02d", i));
|
playFrom.add(String.format(Locale.getDefault(), "代理普畫#%02d%02d", i,index));
|
||||||
}
|
}
|
||||||
return TextUtils.join("$$$", playFrom);
|
return TextUtils.join("$$$", playFrom);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,19 @@
|
||||||
package com.github.catvod.spider;
|
package com.github.catvod.spider;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.text.TextUtils;
|
|
||||||
|
|
||||||
import com.github.catvod.crawler.Spider;
|
import com.github.catvod.crawler.Spider;
|
||||||
import com.github.catvod.crawler.SpiderDebug;
|
import com.github.catvod.crawler.SpiderDebug;
|
||||||
import com.github.catvod.net.OkHttp;
|
import com.github.catvod.net.OkHttp;
|
||||||
import com.github.catvod.utils.Util;
|
import com.github.catvod.utils.Util;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
import java.net.URLEncoder;
|
import java.net.URLEncoder;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
|
@ -29,7 +25,7 @@ public class AppYsV2 extends Spider {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init(Context context, String extend) throws Exception {
|
public void init(Context context, String extend) throws Exception {
|
||||||
super.init(context, extend);
|
super.init(context,extend);
|
||||||
try {
|
try {
|
||||||
extInfos = extend.split("###");
|
extInfos = extend.split("###");
|
||||||
} catch (Exception ignored) {
|
} catch (Exception ignored) {
|
||||||
|
|
@ -365,7 +361,7 @@ public class AppYsV2 extends Spider {
|
||||||
for (int i = 0; i < array.length(); i++) {
|
for (int i = 0; i < array.length(); i++) {
|
||||||
strings.add(array.getString(i));
|
strings.add(array.getString(i));
|
||||||
}
|
}
|
||||||
return TextUtils.join(",", strings);
|
return StringUtils.join(strings, ",");
|
||||||
} catch (JSONException e) {
|
} catch (JSONException e) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
@ -554,11 +550,7 @@ public class AppYsV2 extends Spider {
|
||||||
playFlags.add(flag);
|
playFlags.add(flag);
|
||||||
playUrls.add(from.getString("url"));
|
playUrls.add(from.getString("url"));
|
||||||
String purl = from.optString("parse_api").trim();
|
String purl = from.optString("parse_api").trim();
|
||||||
ArrayList<String> parseUrls = parseUrlMap.get(flag);
|
ArrayList<String> parseUrls = parseUrlMap.get(flag)==null?new ArrayList<>():parseUrlMap.get(flag);
|
||||||
if (parseUrls == null) {
|
|
||||||
parseUrls = new ArrayList<>();
|
|
||||||
parseUrlMap.put(flag, parseUrls);
|
|
||||||
}
|
|
||||||
if (!purl.isEmpty() && !parseUrls.contains(purl)) parseUrls.add(purl);
|
if (!purl.isEmpty() && !parseUrls.contains(purl)) parseUrls.add(purl);
|
||||||
}
|
}
|
||||||
} else if (URL.contains("xgapp")) {
|
} else if (URL.contains("xgapp")) {
|
||||||
|
|
@ -581,11 +573,7 @@ public class AppYsV2 extends Spider {
|
||||||
playFlags.add(flag);
|
playFlags.add(flag);
|
||||||
playUrls.add(from.getString("url"));
|
playUrls.add(from.getString("url"));
|
||||||
String purl = from.optString("parse_api").trim();
|
String purl = from.optString("parse_api").trim();
|
||||||
ArrayList<String> parseUrls = parseUrlMap.get(flag);
|
ArrayList<String> parseUrls = parseUrlMap.get(flag)==null?new ArrayList<>():parseUrlMap.get(flag);
|
||||||
if (parseUrls == null) {
|
|
||||||
parseUrls = new ArrayList<>();
|
|
||||||
parseUrlMap.put(flag, parseUrls);
|
|
||||||
}
|
|
||||||
if (!purl.isEmpty() && !parseUrls.contains(purl)) parseUrls.add(purl);
|
if (!purl.isEmpty() && !parseUrls.contains(purl)) parseUrls.add(purl);
|
||||||
}
|
}
|
||||||
} else if (/*urlPattern2.matcher(URL).find()*/URL.contains(".vod")) {
|
} else if (/*urlPattern2.matcher(URL).find()*/URL.contains(".vod")) {
|
||||||
|
|
@ -613,12 +601,7 @@ public class AppYsV2 extends Spider {
|
||||||
String[] parse2 = from.getJSONObject("player_info").optString("parse2").split(",");
|
String[] parse2 = from.getJSONObject("player_info").optString("parse2").split(",");
|
||||||
parses.addAll(Arrays.asList(parse1));
|
parses.addAll(Arrays.asList(parse1));
|
||||||
parses.addAll(Arrays.asList(parse2));
|
parses.addAll(Arrays.asList(parse2));
|
||||||
ArrayList<String> parseUrls = parseUrlMap.get(flag);
|
ArrayList<String> parseUrls = parseUrlMap.get(flag)==null?new ArrayList<>():parseUrlMap.get(flag); for (String purl : parses) {
|
||||||
if (parseUrls == null) {
|
|
||||||
parseUrls = new ArrayList<>();
|
|
||||||
parseUrlMap.put(flag, parseUrls);
|
|
||||||
}
|
|
||||||
for (String purl : parses) {
|
|
||||||
if (purl.contains("http")) {
|
if (purl.contains("http")) {
|
||||||
Matcher matcher = parsePattern1.matcher(purl);
|
Matcher matcher = parsePattern1.matcher(purl);
|
||||||
if (matcher.find()) {
|
if (matcher.find()) {
|
||||||
|
|
@ -681,11 +664,11 @@ public class AppYsV2 extends Spider {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
playFlags.add(flag);
|
playFlags.add(flag);
|
||||||
playUrls.add(TextUtils.join("#", urls));
|
playUrls.add(StringUtils.join(urls, "#"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
vod.put("vod_play_from", TextUtils.join("$$$", playFlags));
|
vod.put("vod_play_from", StringUtils.join(playFlags, "$$$"));
|
||||||
vod.put("vod_play_url", TextUtils.join("$$$", playUrls));
|
vod.put("vod_play_url", StringUtils.join(playUrls, "$$$"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// ######视频地址
|
// ######视频地址
|
||||||
|
|
@ -782,7 +765,7 @@ public class AppYsV2 extends Spider {
|
||||||
} else if (jsonPlayData.has("User-Agent")) {
|
} else if (jsonPlayData.has("User-Agent")) {
|
||||||
ua = jsonPlayData.optString("User-Agent", "");
|
ua = jsonPlayData.optString("User-Agent", "");
|
||||||
}
|
}
|
||||||
if (ua.trim().length() > 0) {
|
if (!ua.trim().isEmpty()) {
|
||||||
headers.put("User-Agent", " " + ua);
|
headers.put("User-Agent", " " + ua);
|
||||||
}
|
}
|
||||||
String referer = "";
|
String referer = "";
|
||||||
|
|
@ -791,7 +774,7 @@ public class AppYsV2 extends Spider {
|
||||||
} else if (jsonPlayData.has("Referer")) {
|
} else if (jsonPlayData.has("Referer")) {
|
||||||
referer = jsonPlayData.optString("Referer", "");
|
referer = jsonPlayData.optString("Referer", "");
|
||||||
}
|
}
|
||||||
if (referer.trim().length() > 0) {
|
if (!referer.trim().isEmpty()) {
|
||||||
headers.put("Referer", " " + referer);
|
headers.put("Referer", " " + referer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,107 @@
|
||||||
|
package com.github.catvod.spider
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.text.TextUtils
|
||||||
|
import com.github.catvod.api.BaiduDrive
|
||||||
|
import com.github.catvod.api.BaiduDrive.getPlayFormatList
|
||||||
|
import com.github.catvod.api.BaiduDrive.getVod
|
||||||
|
import com.github.catvod.api.BaiduDrive.playerContent
|
||||||
|
import com.github.catvod.api.BaiduDrive.setCookie
|
||||||
|
import com.github.catvod.bean.Result
|
||||||
|
import com.github.catvod.bean.Vod
|
||||||
|
import com.github.catvod.crawler.Spider
|
||||||
|
import com.github.catvod.crawler.SpiderDebug
|
||||||
|
import com.github.catvod.utils.Json
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ColaMint & Adam & FongMi
|
||||||
|
*/
|
||||||
|
class BaiDuPan : Spider() {
|
||||||
|
@Throws(Exception::class)
|
||||||
|
override fun init(context: Context?, extend: String) {
|
||||||
|
setCookie(extend)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ids 為 share_link
|
||||||
|
* @param ids
|
||||||
|
* @return
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
@Throws(Exception::class)
|
||||||
|
override fun detailContent(ids: MutableList<String>): String? {
|
||||||
|
var vod: Vod? = null;
|
||||||
|
runBlocking {
|
||||||
|
vod = getVod(ids[0])
|
||||||
|
}
|
||||||
|
return Result.string(vod);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Throws(Exception::class)
|
||||||
|
override fun playerContent(flag: String, id: String, vipFlags: MutableList<String?>?): String {
|
||||||
|
var content = ""
|
||||||
|
runBlocking {
|
||||||
|
content = playerContent(Json.safeObject(com.github.catvod.utils.Util.base64Decode(id)), flag)
|
||||||
|
}
|
||||||
|
return content
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 獲取詳情內容視頻播放來源(多 shared_link)
|
||||||
|
*
|
||||||
|
* @param ids share_link 集合
|
||||||
|
* @param i
|
||||||
|
* @return 詳情內容視頻播放來源
|
||||||
|
*/
|
||||||
|
fun detailContentVodPlayFrom(ids: MutableList<String?>, index: Int): String? {
|
||||||
|
val playFrom: MutableList<String?> = ArrayList<String?>()
|
||||||
|
|
||||||
|
/* if (ids.size() < 2){
|
||||||
|
return TextUtils.join("$$$", BaiduDrive.get().getPlayFormatList());
|
||||||
|
}*/
|
||||||
|
for (i in 1..ids.size) {
|
||||||
|
playFrom.add( String.format(Locale.getDefault(), "BD原画" + "#%02d_%02d", i, index))
|
||||||
|
/* for (s in getPlayFormatList()) {
|
||||||
|
playFrom.add(String.format(Locale.getDefault(), "BD" + s + "#%02d%02d", i, index))
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
return TextUtils.join("$$$", playFrom)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 獲取詳情內容視頻播放地址(多 share_link)
|
||||||
|
*
|
||||||
|
* @param ids share_link 集合
|
||||||
|
* @return 詳情內容視頻播放地址
|
||||||
|
*/
|
||||||
|
|
||||||
|
fun detailContentVodPlayUrl(ids: List<String>): String? {
|
||||||
|
val playUrl: MutableList<String?> = ArrayList<String?>()
|
||||||
|
for (id in ids) {
|
||||||
|
try {
|
||||||
|
playUrl.add(getVod(id).getVodPlayUrl())
|
||||||
|
} catch (e: Exception) {
|
||||||
|
SpiderDebug.log("获取播放地址出错:" + e.message)
|
||||||
|
playUrl.add("")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return TextUtils.join("$$$", playUrl)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@JvmField
|
||||||
|
var URL_START = "https://pan.baidu.com"
|
||||||
|
|
||||||
|
@Throws(Exception::class)
|
||||||
|
fun proxy(params: MutableMap<String, String>): Array<Any> {
|
||||||
|
val type = params["type"]
|
||||||
|
if ("video" == type) return BaiduDrive.proxyVideo(params)
|
||||||
|
//if ("sub".equals(type)) return AliYun.get().proxySub(params);
|
||||||
|
return arrayOf<Any>()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,150 @@
|
||||||
|
package com.github.catvod.spider;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import com.github.catvod.bean.Class;
|
||||||
|
import com.github.catvod.bean.Result;
|
||||||
|
import com.github.catvod.bean.Vod;
|
||||||
|
import com.github.catvod.net.OkHttp;
|
||||||
|
import com.github.catvod.utils.Json;
|
||||||
|
import com.github.catvod.utils.Util;
|
||||||
|
import com.google.gson.JsonArray;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import org.jsoup.Jsoup;
|
||||||
|
import org.jsoup.nodes.Document;
|
||||||
|
import org.jsoup.nodes.Element;
|
||||||
|
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
public class BiXin extends Cloud {
|
||||||
|
private static final String siteUrl = "https://www.bixbiy.com/";
|
||||||
|
|
||||||
|
private final String hostUrl = siteUrl;
|
||||||
|
|
||||||
|
|
||||||
|
private Map<String, String> getHeader() {
|
||||||
|
Map<String, String> header = new HashMap<>();
|
||||||
|
header.put("User-Agent", Util.CHROME);
|
||||||
|
return header;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, String> getHeaderWithCookie() {
|
||||||
|
Map<String, String> header = new HashMap<>();
|
||||||
|
header.put("User-Agent", Util.CHROME);
|
||||||
|
header.put("cookie", "esc_search_captcha=1; result=43");
|
||||||
|
return header;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(Context context, String extend) throws Exception {
|
||||||
|
|
||||||
|
super.init(context, extend);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String homeContent(boolean filter) {
|
||||||
|
List<Class> classes = new ArrayList<>();
|
||||||
|
String temp = siteUrl + "api/discussions?include=user%2ClastPostedUser%2Ctags%2Ctags.parent%2CfirstPost&sort&page%5Boffset%5D=0";
|
||||||
|
|
||||||
|
String resultStr = OkHttp.string(temp, getHeader());
|
||||||
|
|
||||||
|
classes.add(new Class("mv", "影视"));
|
||||||
|
|
||||||
|
return Result.string(classes, parseVodListFromDoc(resultStr));
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Vod> parseVodListFromDoc(String resultStr) {
|
||||||
|
JsonObject json = Json.safeObject(resultStr);
|
||||||
|
JsonArray arrays = json.get("data").getAsJsonArray();
|
||||||
|
|
||||||
|
List<Vod> list = new ArrayList<>();
|
||||||
|
for (JsonElement array : arrays) {
|
||||||
|
JsonObject data = array.getAsJsonObject();
|
||||||
|
String vodId = data.get("id").getAsString();
|
||||||
|
String vodPic = "";
|
||||||
|
JsonObject attributes = data.get("attributes").getAsJsonObject();
|
||||||
|
String title = attributes.get("title").getAsString();
|
||||||
|
String vodRemarks = "";
|
||||||
|
String vodName = "";
|
||||||
|
if (title.contains("(")) {
|
||||||
|
vodName = title.split("(")[0];
|
||||||
|
vodRemarks = title.split("(")[1];
|
||||||
|
} else {
|
||||||
|
vodName = title;
|
||||||
|
vodRemarks = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
list.add(new Vod(vodId, vodName, vodPic, vodRemarks));
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String categoryContent(String tid, String pg, boolean filter, HashMap<String, String> extend) {
|
||||||
|
int pageSize = 20;
|
||||||
|
String temp = siteUrl +//+ "api/discussions?include=user%2ClastPostedUser%2Ctags%2Ctags.parent%2CfirstPost&filter%5Btag%5D=" + tid + "&sort&page%5Boffset%5D=" + (Integer.parseInt(pg) - 1) * pageSize;
|
||||||
|
"api/discussions?include=user%2ClastPostedUser%2Ctags%2Ctags.parent%2CfirstPost%2Cuser.userBadges%2Cuser.userBadges.badge&filter%5Btag%5D=" + tid + "&sort&page%5Boffset%5D=" + (Integer.parseInt(pg) - 1) * pageSize;
|
||||||
|
String resultStr = OkHttp.string(temp, getHeader());
|
||||||
|
List<Vod> list = parseVodListFromDoc(resultStr);
|
||||||
|
int total = (Integer.parseInt(pg) + 1) * 20;
|
||||||
|
return Result.get().vod(list).page(Integer.parseInt(pg), Integer.parseInt(pg) + 1, pageSize, total).string();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String detailContent(List<String> ids) throws Exception {
|
||||||
|
String vodId = ids.get(0);
|
||||||
|
Document doc = Jsoup.parse(OkHttp.string(siteUrl + "d/" + vodId, getHeader()));
|
||||||
|
|
||||||
|
Vod item = new Vod();
|
||||||
|
item.setVodId(vodId);
|
||||||
|
item.setVodName(doc.selectFirst(" div.container > h1").text());
|
||||||
|
// item.setVodPic(doc.selectFirst(" div.Post-body > p > img").attr("src"));
|
||||||
|
|
||||||
|
|
||||||
|
List<String> shareLinks = new ArrayList<>();
|
||||||
|
for (Element element : doc.select("div.Post-body > p > a")) {
|
||||||
|
if (element.attr("href").contains(YiDongYun.URL_START)) {
|
||||||
|
shareLinks.add(element.attr("href").trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
item.setVodPlayUrl(super.detailContentVodPlayUrl(shareLinks));
|
||||||
|
item.setVodPlayFrom(super.detailContentVodPlayFrom(shareLinks));
|
||||||
|
|
||||||
|
return Result.string(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getStrByRegex(Pattern pattern, String str) {
|
||||||
|
Matcher matcher = pattern.matcher(str);
|
||||||
|
if (matcher.find()) return matcher.group(1).trim();
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String searchContent(String key, boolean quick) throws Exception {
|
||||||
|
return searchContent(key, "1");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String searchContent(String key, boolean quick, String pg) throws Exception {
|
||||||
|
return searchContent(key, pg);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String searchContent(String key, String pg) {
|
||||||
|
int pageSize = 20;
|
||||||
|
String temp = siteUrl + "api/discussions?include=user%2ClastPostedUser%2CmostRelevantPost%2CmostRelevantPost.user%2Ctags%2Ctags.parent%2CfirstPost&filter%5Bq%5D=" + URLEncoder.encode(key) + "&sort&page%5Boffset%5D=" + (Integer.parseInt(pg) - 1) * pageSize;
|
||||||
|
|
||||||
|
String resultStr = OkHttp.string(temp, getHeader());
|
||||||
|
|
||||||
|
|
||||||
|
return Result.string(parseVodListFromDoc(resultStr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,259 @@
|
||||||
|
package com.github.catvod.spider;/*
|
||||||
|
* @File : changzhang.js
|
||||||
|
* @Author : jade
|
||||||
|
* @Date : 2024/2/2 16:02
|
||||||
|
* @Email : jadehh@1ive.com
|
||||||
|
* @Software : Samples
|
||||||
|
* @Desc :
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import com.github.catvod.bean.Class;
|
||||||
|
import com.github.catvod.bean.Filter;
|
||||||
|
import com.github.catvod.bean.Result;
|
||||||
|
import com.github.catvod.bean.Vod;
|
||||||
|
import com.github.catvod.crawler.Spider;
|
||||||
|
import com.github.catvod.net.OkHttp;
|
||||||
|
import com.github.catvod.utils.AESEncryption;
|
||||||
|
import com.github.catvod.utils.Notify;
|
||||||
|
import com.github.catvod.utils.ProxyVideo;
|
||||||
|
import com.github.catvod.utils.Util;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
import org.jsoup.Jsoup;
|
||||||
|
import org.jsoup.nodes.Document;
|
||||||
|
import org.jsoup.nodes.Element;
|
||||||
|
import org.jsoup.select.Elements;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
public class ChangZhang extends Spider {
|
||||||
|
|
||||||
|
private String siteUrl = "https://www.czys.pro";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(Context context, String extend) throws Exception {
|
||||||
|
super.init(context, extend);
|
||||||
|
Document doc = Jsoup.parse(OkHttp.string(extend));
|
||||||
|
|
||||||
|
siteUrl = doc.select("h2 > a").attr("href");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, String> getHeader() {
|
||||||
|
Map<String, String> header = new HashMap<>();
|
||||||
|
header.put("Cookie", "myannoun=1; Hm_lvt_0653ba1ead8a9aabff96252e70492497=2718862211; Hm_lvt_06341c948291d8e90aac72f9d64905b3=2718862211; Hm_lvt_07305e6f6305a01dd93218c7fe6bc9c3=2718862211; Hm_lpvt_07305e6f6305a01dd93218c7fe6bc9c3=2718867254; Hm_lpvt_06341c948291d8e90aac72f9d64905b3=2718867254; Hm_lpvt_0653ba1ead8a9aabff96252e70492497=2718867254");
|
||||||
|
header.put("User-Agent", "Mozilla/5.0 (iPhone; CPU iPhone OS 16_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/100.0.4896.77 Mobile/15E148 Safari/604.1");
|
||||||
|
header.put("Connection", "keep-alive");
|
||||||
|
URI uri = URI.create(siteUrl);
|
||||||
|
header.put("Host", uri.getHost());
|
||||||
|
header.put("Referer", siteUrl + "/");
|
||||||
|
return header;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, String> getIframeHeader(String url) {
|
||||||
|
Map<String, String> header = new HashMap<>();
|
||||||
|
header.put("Cookie", "myannoun=1; Hm_lvt_0653ba1ead8a9aabff96252e70492497=2718862211; Hm_lvt_06341c948291d8e90aac72f9d64905b3=2718862211; Hm_lvt_07305e6f6305a01dd93218c7fe6bc9c3=2718862211; Hm_lpvt_07305e6f6305a01dd93218c7fe6bc9c3=2718867254; Hm_lpvt_06341c948291d8e90aac72f9d64905b3=2718867254; Hm_lpvt_0653ba1ead8a9aabff96252e70492497=2718867254");
|
||||||
|
header.put("User-Agent", Util.CHROME);
|
||||||
|
header.put("Connection", "keep-alive");
|
||||||
|
URI uri = URI.create(url);
|
||||||
|
header.put("Host", uri.getHost());
|
||||||
|
header.put("Sec-Fetch-Dest", "iframe");
|
||||||
|
header.put("sec-fetch-mode", "navigate");
|
||||||
|
header.put("Referer", siteUrl + "/");
|
||||||
|
return header;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, String> getVideoHeader(String url) {
|
||||||
|
Map<String, String> header = new HashMap<>();
|
||||||
|
|
||||||
|
header.put("Accept", "*/*");
|
||||||
|
header.put("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8,zh-TW;q=0.7,de;q=0.6");
|
||||||
|
header.put("Cache-Control", "no-cache");
|
||||||
|
header.put("Connection", "keep-alive");
|
||||||
|
header.put("Pragma", "no-cache");
|
||||||
|
URI uri = URI.create(url);
|
||||||
|
header.put("Host", uri.getHost());
|
||||||
|
header.put("Sec-Fetch-Dest", "video");
|
||||||
|
header.put("Sec-Fetch-Mode", "no-cors");
|
||||||
|
header.put("Sec-Fetch-Site", "cross-site");
|
||||||
|
header.put("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36");
|
||||||
|
/* header.put("sec-ch-ua", "\"Chromium\";v=\"124\", \"Google Chrome\";v=\"124\", \"Not-A.Brand\";v=\"99\"");
|
||||||
|
header.put("sec-ch-ua-mobile", "?0");
|
||||||
|
header.put("sec-ch-ua-platform", "\"Windows\"");*/
|
||||||
|
return header;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String homeContent(boolean filter) throws Exception {
|
||||||
|
|
||||||
|
List<Vod> list = new ArrayList<>();
|
||||||
|
List<Class> classes = new ArrayList<>();
|
||||||
|
LinkedHashMap<String, List<Filter>> filters = new LinkedHashMap<>();
|
||||||
|
Document doc = Jsoup.parse(OkHttp.string(siteUrl));
|
||||||
|
|
||||||
|
for (Element div : doc.select(".navlist > li ")) {
|
||||||
|
classes.add(new Class(div.select(" a").attr("href"), div.select(" a").text()));
|
||||||
|
}
|
||||||
|
|
||||||
|
getVods(list, doc);
|
||||||
|
return Result.string(classes, list);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void getVods(List<Vod> list, Document doc) {
|
||||||
|
for (Element div : doc.select(".bt_img.mi_ne_kd > ul >li")) {
|
||||||
|
String id = div.select(".dytit > a").attr("href");
|
||||||
|
String name = div.select(".dytit > a").text();
|
||||||
|
String pic = div.select("img").attr("data-original");
|
||||||
|
if (pic.isEmpty()) pic = div.select("img").attr("src");
|
||||||
|
String remark = div.select(".hdinfo > span").text();
|
||||||
|
|
||||||
|
list.add(new Vod(id, name, pic, remark));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String categoryContent(String tid, String pg, boolean filter, HashMap<String, String> extend) throws Exception {
|
||||||
|
List<Vod> list = new ArrayList<>();
|
||||||
|
String target = siteUrl + tid + "/page/" + pg;
|
||||||
|
//String filters = extend.get("filters");
|
||||||
|
String html = OkHttp.string(target);
|
||||||
|
Document doc = Jsoup.parse(html);
|
||||||
|
getVods(list, doc);
|
||||||
|
String total = "" + Integer.MAX_VALUE;
|
||||||
|
return Result.get().vod(list).page(Integer.parseInt(pg), Integer.parseInt(total) / 25 + 1, 25, Integer.parseInt(total)).string();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String detailContent(List<String> ids) throws Exception {
|
||||||
|
|
||||||
|
Document doc = Jsoup.parse(OkHttp.string(ids.get(0), getHeader()));
|
||||||
|
|
||||||
|
Elements sources = doc.select("div.paly_list_btn > a");
|
||||||
|
StringBuilder vod_play_url = new StringBuilder();
|
||||||
|
String vod_play_from = "厂长" + "$$$";
|
||||||
|
|
||||||
|
for (int i = 0; i < sources.size(); i++) {
|
||||||
|
String href = sources.get(i).attr("href");
|
||||||
|
String text = sources.get(i).text();
|
||||||
|
vod_play_url.append(text).append("$").append(href);
|
||||||
|
boolean notLastEpisode = i < sources.size() - 1;
|
||||||
|
vod_play_url.append(notLastEpisode ? "#" : "$$$");
|
||||||
|
}
|
||||||
|
|
||||||
|
String title = doc.select(" div.dytext.fl > div > h1").text();
|
||||||
|
String classifyName = doc.select(".moviedteail_list > li:nth-child(1) > a").text();
|
||||||
|
String year = doc.select(".moviedteail_list > li:nth-child(3) > a").text();
|
||||||
|
String area = doc.select(".moviedteail_list > li:nth-child(2) > a").text();
|
||||||
|
String remark = doc.select(".yp_context").text();
|
||||||
|
String vodPic = doc.select(" div.dyxingq > div > div.dyimg.fl > img").attr("src");
|
||||||
|
|
||||||
|
String director = doc.select(".moviedteail_list > li:nth-child(6) > a").text();
|
||||||
|
|
||||||
|
String actor = doc.select(".moviedteail_list > li:nth-child(8) > a").text();
|
||||||
|
|
||||||
|
String brief = doc.select(".yp_context").text();
|
||||||
|
Vod vod = new Vod();
|
||||||
|
vod.setVodId(ids.get(0));
|
||||||
|
vod.setVodYear(year);
|
||||||
|
vod.setVodName(title);
|
||||||
|
vod.setVodArea(area);
|
||||||
|
vod.setVodActor(actor);
|
||||||
|
vod.setVodPic(vodPic);
|
||||||
|
vod.setVodRemarks(remark);
|
||||||
|
vod.setVodContent(brief);
|
||||||
|
vod.setVodDirector(director);
|
||||||
|
vod.setTypeName(classifyName);
|
||||||
|
vod.setVodPlayFrom(vod_play_from);
|
||||||
|
vod.setVodPlayUrl(vod_play_url.toString());
|
||||||
|
return Result.string(vod);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String searchContent(String key, boolean quick) throws Exception {
|
||||||
|
String searchUrl = siteUrl + "/daoyongjiekoshibushiyoubing?q=";
|
||||||
|
String html = OkHttp.string(searchUrl + key);
|
||||||
|
if (html.contains("Just a moment")) {
|
||||||
|
Notify.show("厂长资源需要人机验证");
|
||||||
|
}
|
||||||
|
Document document = Jsoup.parse(html);
|
||||||
|
List<Vod> list = new ArrayList<>();
|
||||||
|
getVods(list, document);
|
||||||
|
|
||||||
|
return Result.string(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String playerContent(String flag, String id, List<String> vipFlags) throws Exception {
|
||||||
|
String content = OkHttp.string(id, getHeader());
|
||||||
|
Document document = Jsoup.parse(content);
|
||||||
|
Elements iframe = document.select("iframe");
|
||||||
|
if (!iframe.isEmpty()) {
|
||||||
|
String videoContent = OkHttp.string(iframe.get(0).attr("src"), getIframeHeader(iframe.get(0).attr("src")));
|
||||||
|
|
||||||
|
|
||||||
|
Matcher matcher2 = Pattern.compile("result_v2 =(.*?);").matcher(videoContent);
|
||||||
|
String json2 = matcher2.find() ? matcher2.group(1) : "";
|
||||||
|
org.json.JSONObject jsonObject = new JSONObject(json2);
|
||||||
|
String encodedStr = jsonObject.getString("data");
|
||||||
|
String realUrl = new String(new BigInteger(StringUtils.reverse(encodedStr), 16).toByteArray());
|
||||||
|
|
||||||
|
String temp = decodeStr(realUrl);
|
||||||
|
Map<String, String> header = getVideoHeader(temp);
|
||||||
|
return Result.get().url(ProxyVideo.buildCommonProxyUrl(temp, header)).string();
|
||||||
|
} else {
|
||||||
|
for (Element script : document.select("script")) {
|
||||||
|
String scriptText = script.html();
|
||||||
|
if (scriptText.contains("wp_nonce")) {
|
||||||
|
String reg = "var(.*?)=\"(.*?)\"";
|
||||||
|
Pattern pattern = Pattern.compile(reg);
|
||||||
|
Matcher matcher = pattern.matcher(scriptText);
|
||||||
|
|
||||||
|
if (matcher.find()) {
|
||||||
|
String data = matcher.group(2);
|
||||||
|
String result = dncry(data);
|
||||||
|
String regex = "url:.*?['\"](.*?)['\"]";
|
||||||
|
Pattern pattern1 = Pattern.compile(regex);
|
||||||
|
Matcher matcher1 = pattern1.matcher(result);
|
||||||
|
if (matcher1.find()) {
|
||||||
|
String playUrl = matcher1.group(0).replace("\"", "").replace("url:", "").trim();
|
||||||
|
return Result.get().url(playUrl).string();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String dncry(String data) {
|
||||||
|
String kc8a64 = "336460fdcb76a597";
|
||||||
|
String iv = "1234567890983456";
|
||||||
|
|
||||||
|
return AESEncryption.decrypt(data, kc8a64, iv,AESEncryption.CBC_PKCS_7_PADDING);
|
||||||
|
}
|
||||||
|
|
||||||
|
;
|
||||||
|
|
||||||
|
String decodeStr(String _0x267828) {
|
||||||
|
int _0x5cd2b5 = (_0x267828.length() - 7) / 2;
|
||||||
|
String _0x2191ed = _0x267828.substring(0, _0x5cd2b5);
|
||||||
|
String _0x35a256 = _0x267828.substring(_0x5cd2b5 + 7);
|
||||||
|
return _0x2191ed + _0x35a256;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,192 @@
|
||||||
|
package com.github.catvod.spider;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import com.github.catvod.api.Pan123Api;
|
||||||
|
import com.github.catvod.crawler.Spider;
|
||||||
|
import com.github.catvod.crawler.SpiderDebug;
|
||||||
|
import com.github.catvod.utils.Json;
|
||||||
|
import com.github.catvod.utils.Util;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.apache.commons.lang3.tuple.ImmutablePair;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
|
||||||
|
import static com.github.catvod.api.TianyiApi.URL_CONTAIN;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ColaMint & Adam & FongMi
|
||||||
|
*/
|
||||||
|
public class Cloud extends Spider {
|
||||||
|
private Quark quark = null;
|
||||||
|
/* private Ali ali = null;*/
|
||||||
|
private UC uc = null;
|
||||||
|
private TianYi tianYi = null;
|
||||||
|
private YiDongYun yiDongYun = null;
|
||||||
|
private BaiDuPan baiDuPan = null;
|
||||||
|
private Pan123 pan123 = null;
|
||||||
|
private static final Map<String, ImmutablePair<List<String>, List<String>>> resultMap = new HashMap<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(Context context, String extend) throws Exception {
|
||||||
|
JsonObject ext = Json.safeObject(extend);
|
||||||
|
quark = new Quark();
|
||||||
|
uc = new UC();
|
||||||
|
/* ali = new Ali();*/
|
||||||
|
tianYi = new TianYi();
|
||||||
|
yiDongYun = new YiDongYun();
|
||||||
|
baiDuPan = new BaiDuPan();
|
||||||
|
pan123 = new Pan123();
|
||||||
|
boolean first = Objects.nonNull(ext);
|
||||||
|
quark.init(context, first && ext.has("cookie") ? ext.get("cookie").getAsString() : "");
|
||||||
|
uc.init(context, first && ext.has("uccookie") ? ext.get("uccookie").getAsString() : "");
|
||||||
|
/* ali.init(context, first && ext.has("token") ? ext.get("token").getAsString() : "");*/
|
||||||
|
tianYi.init(context, first && ext.has("tianyicookie") ? ext.get("tianyicookie").getAsString() : "");
|
||||||
|
yiDongYun.init(context, "");
|
||||||
|
baiDuPan.init(context, "");
|
||||||
|
pan123.init(context, "");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String detailContent(List<String> shareUrl) throws Exception {
|
||||||
|
SpiderDebug.log("cloud detailContent shareUrl:" + Json.toJson(shareUrl));
|
||||||
|
|
||||||
|
/* if (shareUrl.get(0).matches(Util.patternAli)) {
|
||||||
|
return ali.detailContent(shareUrl);
|
||||||
|
} else */
|
||||||
|
if (shareUrl.get(0).matches(Util.patternQuark)) {
|
||||||
|
return quark.detailContent(shareUrl);
|
||||||
|
} else if (shareUrl.get(0).matches(Util.patternUC)) {
|
||||||
|
return uc.detailContent(shareUrl);
|
||||||
|
} else if (shareUrl.get(0).contains(URL_CONTAIN)) {
|
||||||
|
return tianYi.detailContent(shareUrl);
|
||||||
|
} else if (shareUrl.get(0).contains(YiDongYun.URL_START)) {
|
||||||
|
return yiDongYun.detailContent(shareUrl);
|
||||||
|
} else if (shareUrl.get(0).contains(BaiDuPan.URL_START)) {
|
||||||
|
return baiDuPan.detailContent(shareUrl);
|
||||||
|
} else if (shareUrl.get(0).matches(Pan123Api.regex)) {
|
||||||
|
SpiderDebug.log("Pan123Api shareUrl:" + Json.toJson(shareUrl));
|
||||||
|
return pan123.detailContent(shareUrl);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String playerContent(String flag, String id, List<String> vipFlags) throws Exception {
|
||||||
|
SpiderDebug.log("cloud playerContent flag:" + flag + " id:" + id);
|
||||||
|
|
||||||
|
if (flag.contains("quark")) {
|
||||||
|
return quark.playerContent(flag, id, vipFlags);
|
||||||
|
} else if (flag.contains("uc")) {
|
||||||
|
return uc.playerContent(flag, id, vipFlags);
|
||||||
|
} else if (flag.contains("天意")) {
|
||||||
|
return tianYi.playerContent(flag, id, vipFlags);
|
||||||
|
} else if (flag.contains("移动")) {
|
||||||
|
return yiDongYun.playerContent(flag, id, vipFlags);
|
||||||
|
}/* else {
|
||||||
|
return ali.playerContent(flag, id, vipFlags);
|
||||||
|
}*/ else if (flag.contains("BD")) {
|
||||||
|
return baiDuPan.playerContent(flag, id, vipFlags);
|
||||||
|
} else if (flag.contains("pan123")) {
|
||||||
|
return pan123.playerContent(flag, id, vipFlags);
|
||||||
|
}
|
||||||
|
return flag;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String detailContentVodPlayFrom(List<String> shareLinks) {
|
||||||
|
ImmutablePair<List<String>, List<String>> pairs = resultMap.get(Util.MD5(Json.toJson(shareLinks)));
|
||||||
|
if (pairs != null && pairs.left != null && !pairs.left.isEmpty()) {
|
||||||
|
return TextUtils.join("$$$", pairs.right);
|
||||||
|
}
|
||||||
|
|
||||||
|
getPlayFromAndUrl(shareLinks);
|
||||||
|
pairs = resultMap.get(Util.MD5(Json.toJson(shareLinks)));
|
||||||
|
if (pairs != null && pairs.left != null && !pairs.left.isEmpty()) {
|
||||||
|
return TextUtils.join("$$$", pairs.right);
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String detailContentVodPlayUrl(List<String> shareLinks) {
|
||||||
|
ImmutablePair<List<String>, List<String>> pairs = resultMap.get(Util.MD5(Json.toJson(shareLinks)));
|
||||||
|
if (pairs != null && pairs.left != null && !pairs.left.isEmpty()) {
|
||||||
|
return TextUtils.join("$$$", pairs.left);
|
||||||
|
}
|
||||||
|
|
||||||
|
getPlayFromAndUrl(shareLinks);
|
||||||
|
pairs = resultMap.get(Util.MD5(Json.toJson(shareLinks)));
|
||||||
|
if (pairs != null && pairs.left != null && !pairs.left.isEmpty()) {
|
||||||
|
return TextUtils.join("$$$", pairs.left);
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//同時获取from 和url ,放入缓存,只要一个函数执行就行,避免重复执行
|
||||||
|
private void getPlayFromAndUrl(List<String> shareLinks) {
|
||||||
|
ExecutorService service = Executors.newFixedThreadPool(4);
|
||||||
|
try { //首先清空缓存,避免太多缓存
|
||||||
|
resultMap.clear();
|
||||||
|
List<String> urls = new ArrayList<>();
|
||||||
|
List<String> froms = new ArrayList<>();
|
||||||
|
|
||||||
|
List<Future<ImmutablePair<String, String>>> futures = new ArrayList<>();
|
||||||
|
int i = 0;
|
||||||
|
for (String shareLink : shareLinks) {
|
||||||
|
|
||||||
|
int finalI = ++i;
|
||||||
|
futures.add(service.submit(() -> {
|
||||||
|
|
||||||
|
String url = "";
|
||||||
|
String from = "";
|
||||||
|
if (shareLink.matches(Util.patternUC)) {
|
||||||
|
url = uc.detailContentVodPlayUrl(List.of(shareLink));
|
||||||
|
from = uc.detailContentVodPlayFrom(List.of(shareLink), finalI);
|
||||||
|
} else if (shareLink.matches(Util.patternQuark)) {
|
||||||
|
url = quark.detailContentVodPlayUrl(List.of(shareLink));
|
||||||
|
from = quark.detailContentVodPlayFrom(List.of(shareLink), finalI);
|
||||||
|
}/* else if (shareLink.matches(Util.patternAli)) {
|
||||||
|
urls.add(ali.detailContentVodPlayUrl(List.of(shareLink)));
|
||||||
|
} */ else if (shareLink.contains(URL_CONTAIN)) {
|
||||||
|
url = tianYi.detailContentVodPlayUrl(List.of(shareLink));
|
||||||
|
from = tianYi.detailContentVodPlayFrom(List.of(shareLink), finalI);
|
||||||
|
} else if (shareLink.contains(YiDongYun.URL_START)) {
|
||||||
|
url = yiDongYun.detailContentVodPlayUrl(List.of(shareLink));
|
||||||
|
from = yiDongYun.detailContentVodPlayFrom(List.of(shareLink), finalI);
|
||||||
|
} else if (shareLink.contains(BaiDuPan.URL_START)) {
|
||||||
|
url = baiDuPan.detailContentVodPlayUrl(List.of(shareLink));
|
||||||
|
from = baiDuPan.detailContentVodPlayFrom(List.of(shareLink), finalI);
|
||||||
|
} else if (shareLink.matches(Pan123Api.regex)) {
|
||||||
|
url = pan123.detailContentVodPlayUrl(List.of(shareLink));
|
||||||
|
from = pan123.detailContentVodPlayFrom(List.of(shareLink), finalI);
|
||||||
|
}
|
||||||
|
return new ImmutablePair<>(url, from);
|
||||||
|
}));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Future<ImmutablePair<String, String>> future : futures) {
|
||||||
|
//只有连接不为空才放入进去
|
||||||
|
if (StringUtils.isNoneBlank(future.get().left)) {
|
||||||
|
urls.add(future.get().left);
|
||||||
|
froms.add(future.get().right);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
resultMap.put(Util.MD5(Json.toJson(shareLinks)), new ImmutablePair<>(urls, froms));
|
||||||
|
|
||||||
|
SpiderDebug.log("---urls:" + Json.toJson(urls));
|
||||||
|
SpiderDebug.log("---froms:" + Json.toJson(froms));
|
||||||
|
} catch (Exception e) {
|
||||||
|
SpiderDebug.log("获取异步结果出错:" + e);
|
||||||
|
} finally {
|
||||||
|
service.shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,195 @@
|
||||||
|
package com.github.catvod.spider;
|
||||||
|
|
||||||
|
import com.github.catvod.bean.Class;
|
||||||
|
import com.github.catvod.bean.Result;
|
||||||
|
import com.github.catvod.bean.Vod;
|
||||||
|
import com.github.catvod.crawler.Spider;
|
||||||
|
import com.github.catvod.net.OkHttp;
|
||||||
|
import com.github.catvod.utils.AESEncryption;
|
||||||
|
import com.github.catvod.utils.Util;
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
|
||||||
|
import org.jsoup.Jsoup;
|
||||||
|
import org.jsoup.nodes.Document;
|
||||||
|
import org.jsoup.nodes.Element;
|
||||||
|
import org.jsoup.select.Elements;
|
||||||
|
|
||||||
|
import java.net.URLDecoder;
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
public class DaGongRen extends Spider {
|
||||||
|
|
||||||
|
private static final String siteUrl = "https://dagongren1.com";
|
||||||
|
private static final String cateUrl = siteUrl + "/list/";
|
||||||
|
private static final String detailUrl = siteUrl + "/play/";
|
||||||
|
private static final String playUrl = siteUrl + "/play/";
|
||||||
|
private static final String searchUrl = siteUrl + "/search--------------.html?wd=";
|
||||||
|
|
||||||
|
private HashMap<String, String> getHeaders() {
|
||||||
|
HashMap<String, String> headers = new HashMap<>();
|
||||||
|
headers.put("User-Agent", Util.CHROME);
|
||||||
|
headers.put("Host", "dagongren1.com");
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String homeContent(boolean filter) throws Exception {
|
||||||
|
List<Vod> list = new ArrayList<>();
|
||||||
|
List<Class> classes = new ArrayList<>();
|
||||||
|
String[] typeIdList = {"dianying","dianshiju","zongyi","dongman","jilupian","lunlipian"};
|
||||||
|
String[] typeNameList = {"电影","连续剧","综艺","动漫","纪录片","福利"};
|
||||||
|
for (int i = 0; i < typeNameList.length; i++) {
|
||||||
|
classes.add(new Class(typeIdList[i], typeNameList[i]));
|
||||||
|
}
|
||||||
|
Document doc = Jsoup.parse(OkHttp.string(siteUrl, getHeaders()));
|
||||||
|
for (Element element : doc.select("a.vodlist_thumb")) {
|
||||||
|
try {
|
||||||
|
String pic = element.attr("data-original");
|
||||||
|
String url = element.attr("href");
|
||||||
|
String name = element.attr("title");
|
||||||
|
if (!pic.startsWith("http")) {
|
||||||
|
pic = siteUrl + pic;
|
||||||
|
}
|
||||||
|
String id = url.split("/")[2];
|
||||||
|
list.add(new Vod(id, name, pic));
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Result.string(classes, list);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String MD5(String string) {
|
||||||
|
// 创建 MD5 实例
|
||||||
|
try {
|
||||||
|
MessageDigest md = MessageDigest.getInstance("MD5");
|
||||||
|
// 计算 MD5 哈希值
|
||||||
|
byte[] hashBytes = md.digest(string.getBytes());
|
||||||
|
|
||||||
|
// 将字节数组转换为十六进制字符串表示
|
||||||
|
StringBuilder hexString = new StringBuilder();
|
||||||
|
for (byte hashByte : hashBytes) {
|
||||||
|
String hex = Integer.toHexString(0xff & hashByte);
|
||||||
|
if (hex.length() == 1) {
|
||||||
|
hexString.append('0');
|
||||||
|
}
|
||||||
|
hexString.append(hex);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 输出加密后的 MD5 字符串
|
||||||
|
System.out.println("MD5 加密: " + hexString.toString());
|
||||||
|
return hexString.toString();
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String categoryContent(String tid, String pg, boolean filter, HashMap<String, String> extend) throws Exception {
|
||||||
|
List<Vod> list = new ArrayList<>();
|
||||||
|
tid = "/show-" + tid + "--------" + pg + "---.html";
|
||||||
|
String target = siteUrl + tid;
|
||||||
|
Document doc = Jsoup.parse(OkHttp.string(target, getHeaders()));
|
||||||
|
for (Element element : doc.select("a.vodlist_thumb")) {
|
||||||
|
try {
|
||||||
|
String pic = element.attr("data-original");
|
||||||
|
String url = element.attr("href");
|
||||||
|
String name = element.attr("title");
|
||||||
|
if (!pic.startsWith("http")) {
|
||||||
|
pic = siteUrl + pic;
|
||||||
|
}
|
||||||
|
String id = url.split("/")[2];
|
||||||
|
list.add(new Vod(id, name, pic));
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Integer total = (Integer.parseInt(pg) + 1) * 20;
|
||||||
|
return Result.string(Integer.parseInt(pg), Integer.parseInt(pg) + 1, 20, total, list);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String detailContent(List<String> ids) throws Exception {
|
||||||
|
Document doc = Jsoup.parse(OkHttp.string(detailUrl.concat(ids.get(0)), getHeaders()));
|
||||||
|
String name = doc.select("h2.title.margin_0").text();
|
||||||
|
String pic = doc.select("div.play_vlist_thumb").get(0).attr("data-original");
|
||||||
|
// 播放源
|
||||||
|
Elements tabs = doc.select("li.tab-play");
|
||||||
|
Elements list = doc.select("ul.content_playlist");
|
||||||
|
String PlayFrom = "";
|
||||||
|
String PlayUrl = "";
|
||||||
|
for (int i = 0; i < tabs.size(); i++) {
|
||||||
|
String tabName = tabs.get(i).text();
|
||||||
|
if (!"".equals(PlayFrom)) {
|
||||||
|
PlayFrom = PlayFrom + "$$$" + tabName;
|
||||||
|
} else {
|
||||||
|
PlayFrom = PlayFrom + tabName;
|
||||||
|
}
|
||||||
|
Elements li = list.get(i).select("a");
|
||||||
|
String liUrl = "";
|
||||||
|
for (int i1 = 0; i1 < li.size(); i1++) {
|
||||||
|
if (!"".equals(liUrl)) {
|
||||||
|
liUrl = liUrl + "#" + li.get(i1).text() + "$" + li.get(i1).attr("href").replace("/play/","");
|
||||||
|
} else {
|
||||||
|
liUrl = liUrl + li.get(i1).text() + "$" + li.get(i1).attr("href").replace("/play/","");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!"".equals(PlayUrl)) {
|
||||||
|
PlayUrl = PlayUrl + "$$$" + liUrl;
|
||||||
|
} else {
|
||||||
|
PlayUrl = PlayUrl + liUrl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Vod vod = new Vod();
|
||||||
|
vod.setVodId(ids.get(0));
|
||||||
|
vod.setVodPic(siteUrl + pic);
|
||||||
|
vod.setVodName(name);
|
||||||
|
vod.setVodPlayFrom(PlayFrom);
|
||||||
|
vod.setVodPlayUrl(PlayUrl);
|
||||||
|
return Result.string(vod);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String searchContent(String key, boolean quick) throws Exception {
|
||||||
|
List<Vod> list = new ArrayList<>();
|
||||||
|
Document doc = Jsoup.parse(OkHttp.string(searchUrl.concat(URLEncoder.encode(key)), getHeaders()));
|
||||||
|
for (Element element : doc.select("div.searchlist_img")) {
|
||||||
|
try {
|
||||||
|
String pic = element.select("a").attr("data-original");
|
||||||
|
String url = element.select("a").attr("href");
|
||||||
|
String name = element.select("a").attr("title");
|
||||||
|
if (!pic.startsWith("http")) {
|
||||||
|
pic = siteUrl + pic;
|
||||||
|
}
|
||||||
|
String id = url.replace("/video/","").replace(".html","-1-1.html");
|
||||||
|
list.add(new Vod(id, name, pic));
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Result.string(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String playerContent(String flag, String id, List<String> vipFlags) throws Exception {
|
||||||
|
String target = playUrl.concat(id);
|
||||||
|
Document doc = Jsoup.parse(OkHttp.string(target));
|
||||||
|
String regex = "\"url\\\":\\\"(.*?)\\\",\\\"url_next\\\":";
|
||||||
|
|
||||||
|
Pattern pattern = Pattern.compile(regex);
|
||||||
|
Matcher matcher = pattern.matcher(doc.html());
|
||||||
|
String url = "";
|
||||||
|
if (matcher.find()) {
|
||||||
|
url = URLDecoder.decode(matcher.group(1), "UTF-8").split("&")[0];
|
||||||
|
}
|
||||||
|
return Result.get().url(url).header(getHeaders()).string();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,439 @@
|
||||||
|
package com.github.catvod.spider;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import com.github.catvod.bean.Class;
|
||||||
|
import com.github.catvod.bean.Result;
|
||||||
|
import com.github.catvod.bean.Vod;
|
||||||
|
import com.github.catvod.crawler.SpiderDebug;
|
||||||
|
import com.github.catvod.net.OkHttp;
|
||||||
|
import com.github.catvod.utils.Json;
|
||||||
|
import com.github.catvod.utils.ProxyVideo;
|
||||||
|
import com.github.catvod.utils.Util;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
import org.jsoup.Jsoup;
|
||||||
|
import org.jsoup.nodes.Document;
|
||||||
|
import org.jsoup.nodes.Element;
|
||||||
|
import org.jsoup.select.Elements;
|
||||||
|
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Demo for self study
|
||||||
|
* <p>
|
||||||
|
* Source from Author: CatVod
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class Ddrk extends Cloud {
|
||||||
|
|
||||||
|
private static String siteUrl = "https://ddys.pro";
|
||||||
|
|
||||||
|
|
||||||
|
protected JSONObject filterConfig;
|
||||||
|
|
||||||
|
protected Pattern regexCategory = Pattern.compile("/category/(\\S+)/");
|
||||||
|
protected Pattern regexVid = Pattern.compile("https://ddys.pro/(\\S+)/");
|
||||||
|
|
||||||
|
protected Pattern regexPage = Pattern.compile("\\S+/page/(\\S+)\\S+");
|
||||||
|
protected Pattern m = Pattern.compile("\\S+(http\\S+g)");
|
||||||
|
protected Pattern mark = Pattern.compile("\\S+(.*)");
|
||||||
|
|
||||||
|
// protected Pattern t = Pattern.compile("(\\S+)");
|
||||||
|
|
||||||
|
protected static HashMap<String, String> Headers() {
|
||||||
|
HashMap<String, String> headers = new HashMap<>();
|
||||||
|
headers.put("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36");
|
||||||
|
headers.put("Referer", siteUrl);
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String doReplaceRegex(Pattern pattern, String src) {
|
||||||
|
if (pattern == null) return src;
|
||||||
|
try {
|
||||||
|
Matcher matcher = pattern.matcher(src);
|
||||||
|
if (matcher.find()) {
|
||||||
|
return matcher.group(1).trim();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
SpiderDebug.log(e);
|
||||||
|
}
|
||||||
|
return src;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(Context context, String extend) throws Exception {
|
||||||
|
super.init(context, extend);
|
||||||
|
JsonElement json = Json.parse(extend);
|
||||||
|
String html = OkHttp.string(json.getAsJsonObject().get("site").getAsString());
|
||||||
|
Document doc = Jsoup.parse(html);
|
||||||
|
for (Element element : doc.select("a")) {
|
||||||
|
if (element.text().contains("https")) {
|
||||||
|
siteUrl = element.attr("href");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SpiderDebug.log("ddys =====>" + siteUrl); // js_debug.log
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 爬虫headers
|
||||||
|
*
|
||||||
|
* @param url
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
protected HashMap<String, String> getHeaders(String url) {
|
||||||
|
HashMap<String, String> headers = new HashMap<>();
|
||||||
|
headers.put("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.62 Safari/537.36");
|
||||||
|
headers.put("Referer", siteUrl);
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取分类数据 + 首页最近更新视频列表数据
|
||||||
|
*
|
||||||
|
* @param filter 是否开启筛选 关联的是 软件设置中 首页数据源里的筛选开关
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String homeContent(boolean filter) {
|
||||||
|
|
||||||
|
List<Vod> vods = new ArrayList<>();
|
||||||
|
String url = siteUrl + '/';
|
||||||
|
Document doc = Jsoup.parse(OkHttp.string(url, getHeaders(url)));
|
||||||
|
Elements elements = doc.select("li.menu-item a");
|
||||||
|
List<Class> classes = new ArrayList<>();
|
||||||
|
|
||||||
|
for (Element ele : elements) {
|
||||||
|
String name = ele.attr("title");
|
||||||
|
String id = ele.attr("href");
|
||||||
|
if (ele.attr("href").contains("category") || ele.attr("href").contains("tag")) {
|
||||||
|
classes.add(new Class(id, name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 取首页推荐视频列表
|
||||||
|
Elements list = doc.select("div.post-box-container");
|
||||||
|
|
||||||
|
for (int i = 0; i < list.size(); i++) {
|
||||||
|
Element vod = list.get(i);
|
||||||
|
String title = vod.selectFirst(".post-box-title > a").text();
|
||||||
|
String id = vod.selectFirst(".post-box-title > a").attr("href");
|
||||||
|
String imageHtml = vod.selectFirst("div.post-box-image").attr("style");
|
||||||
|
String image = "";
|
||||||
|
String regex = "url\\((.*?)\\)";
|
||||||
|
|
||||||
|
Pattern pattern = Pattern.compile(regex);
|
||||||
|
Matcher matcher = pattern.matcher(imageHtml);
|
||||||
|
if (matcher.find()) {
|
||||||
|
image = matcher.group(1);
|
||||||
|
}
|
||||||
|
vods.add(new Vod(id, title, image));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return Result.string(classes, vods, filterConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取分类信息数据
|
||||||
|
*
|
||||||
|
* @param tid 分类id
|
||||||
|
* @param pg 页数
|
||||||
|
* @param filter 同homeContent方法中的filter
|
||||||
|
* @param extend 筛选参数{k:v, k1:v1}
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String categoryContent(String tid, String pg, boolean filter, HashMap<String, String> extend) {
|
||||||
|
String url = "";
|
||||||
|
try {
|
||||||
|
if (extend != null && extend.size() > 0) {
|
||||||
|
for (Iterator<String> it = extend.keySet().iterator(); it.hasNext(); ) {
|
||||||
|
String key = it.next();
|
||||||
|
String value = extend.get(key);
|
||||||
|
if (value != null && value.length() != 0 && value != " ") {
|
||||||
|
url = siteUrl + "/category/" + tid + "/" + value;
|
||||||
|
} else {
|
||||||
|
url = siteUrl + "/category/" + tid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
url = tid;
|
||||||
|
}
|
||||||
|
if (pg.equals("1")) {
|
||||||
|
url = url + "/";
|
||||||
|
} else {
|
||||||
|
url = url + "/page/" + pg + "/";
|
||||||
|
}
|
||||||
|
//System.out.println(url);
|
||||||
|
String html = OkHttp.string(url, getHeaders(url));
|
||||||
|
Document doc = Jsoup.parse(html);
|
||||||
|
JSONObject result = new JSONObject();
|
||||||
|
int pageCount = 0;
|
||||||
|
int page = -1;
|
||||||
|
|
||||||
|
// 取页码相关信息
|
||||||
|
Elements pageInfo = doc.select("div.nav-links");
|
||||||
|
if (pageInfo.size() == 0) {
|
||||||
|
page = Integer.parseInt(pg);
|
||||||
|
pageCount = page;
|
||||||
|
} else {
|
||||||
|
for (int i = 0; i < pageInfo.size(); i++) {
|
||||||
|
Element li = pageInfo.get(i);
|
||||||
|
Element a = li.selectFirst("a");
|
||||||
|
if (a == null) continue;
|
||||||
|
String wy = doc.select("div.nav-links a").last().attr("href");
|
||||||
|
String span = doc.select("span.current").text().trim();
|
||||||
|
if (page == -1) {
|
||||||
|
page = Integer.parseInt(span);
|
||||||
|
} else {
|
||||||
|
|
||||||
|
page = 0;
|
||||||
|
}
|
||||||
|
Matcher matcher = regexPage.matcher(wy);
|
||||||
|
if (matcher.find()) {
|
||||||
|
//System.out.println("尾页" + matcher.group(1));
|
||||||
|
pageCount = Integer.parseInt(matcher.group(1));
|
||||||
|
} else {
|
||||||
|
pageCount = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONArray videos = new JSONArray();
|
||||||
|
if (!html.contains("没有找到您想要的结果哦")) {
|
||||||
|
// 取当前分类页的视频列表
|
||||||
|
Elements list = doc.select("div.post-box-container");
|
||||||
|
for (int i = 0; i < list.size(); i++) {
|
||||||
|
Element vod = list.get(i);
|
||||||
|
String a = vod.selectFirst(".post-box-title a").text();
|
||||||
|
if (a.contains("(")) {
|
||||||
|
String[] item = a.split("\\(");
|
||||||
|
String title = item[0];
|
||||||
|
String remark = item[1].replace(")", "");
|
||||||
|
String cover = doReplaceRegex(m, vod.selectFirst(".post-box-image").attr("style"));
|
||||||
|
|
||||||
|
String id = vod.selectFirst(".post-box-title a").attr("href");
|
||||||
|
JSONObject v = new JSONObject();
|
||||||
|
v.put("vod_id", id);
|
||||||
|
v.put("vod_name", title);
|
||||||
|
v.put("vod_pic", cover);
|
||||||
|
v.put("vod_remarks", remark);
|
||||||
|
videos.put(v);
|
||||||
|
} else {
|
||||||
|
String title = a;
|
||||||
|
String cover = doReplaceRegex(m, vod.selectFirst(".post-box-image").attr("style"));
|
||||||
|
String remark = doReplaceRegex(mark, vod.selectFirst(".post-box-title a").text());
|
||||||
|
Matcher matcher = regexVid.matcher(vod.selectFirst(".post-box-title a").attr("href"));
|
||||||
|
if (!matcher.find()) continue;
|
||||||
|
String id = matcher.group(1);
|
||||||
|
JSONObject v = new JSONObject();
|
||||||
|
v.put("vod_id", id);
|
||||||
|
v.put("vod_name", title);
|
||||||
|
v.put("vod_pic", cover);
|
||||||
|
v.put("vod_remarks", remark);
|
||||||
|
videos.put(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.put("page", page);
|
||||||
|
result.put("pagecount", pageCount);
|
||||||
|
result.put("limit", 24);
|
||||||
|
result.put("total", pageCount <= 1 ? videos.length() : pageCount * 24);
|
||||||
|
|
||||||
|
result.put("list", videos);
|
||||||
|
return result.toString();
|
||||||
|
} catch (Exception e) {
|
||||||
|
SpiderDebug.log(e);
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 视频详情信息
|
||||||
|
*
|
||||||
|
* @param ids 视频id
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String detailContent(List<String> ids) {
|
||||||
|
try {
|
||||||
|
// 视频详情url
|
||||||
|
String url = ids.get(0);
|
||||||
|
if (!url.startsWith("http")) {
|
||||||
|
url = siteUrl +"/"+ url + "/";
|
||||||
|
}
|
||||||
|
Document doc = Jsoup.parse(OkHttp.string(url, getHeaders(url)));
|
||||||
|
JSONObject result = new JSONObject();
|
||||||
|
JSONObject vodList = new JSONObject();
|
||||||
|
|
||||||
|
// 取基本数据
|
||||||
|
String cover = doc.select("div.post img").attr("src");
|
||||||
|
String ab = doc.select("h1.post-title").text();
|
||||||
|
if (ab.contains("(")) {
|
||||||
|
String[] b = ab.split("\\(");
|
||||||
|
String title = b[0];
|
||||||
|
String remark = b[1].replace("(", "");
|
||||||
|
vodList.put("vod_name", title);
|
||||||
|
vodList.put("vod_remarks", remark);
|
||||||
|
} else {
|
||||||
|
vodList.put("vod_name", ab);
|
||||||
|
String remark = doc.select("time").text().trim();
|
||||||
|
vodList.put("vod_remarks", "全");
|
||||||
|
}
|
||||||
|
String str2 = doc.select("div.abstract").text().replace(" ", "");
|
||||||
|
String replace = str2.replace("<br>", "");
|
||||||
|
String text = replace.replace("<p></p>", "");
|
||||||
|
Pattern categorys = Pattern.compile("类型:(.*)制");
|
||||||
|
String category = doReplaceRegex(categorys, text);
|
||||||
|
Pattern a = Pattern.compile("年份:(.*)简");
|
||||||
|
String year = doReplaceRegex(a, text);
|
||||||
|
Pattern b = Pattern.compile("地区:(.*)年份");
|
||||||
|
String area = doReplaceRegex(b, text);
|
||||||
|
Pattern c = Pattern.compile("演员:(.*)类");
|
||||||
|
String actor = doReplaceRegex(c, text);
|
||||||
|
Pattern d = Pattern.compile("导演:(.*)演");
|
||||||
|
String director = doReplaceRegex(d, text);
|
||||||
|
Pattern e = Pattern.compile("简介:(.*)");
|
||||||
|
String desc = doReplaceRegex(e, text);
|
||||||
|
|
||||||
|
|
||||||
|
vodList.put("vod_id", ids.get(0));
|
||||||
|
vodList.put("vod_pic", cover);
|
||||||
|
vodList.put("type_name", category);
|
||||||
|
vodList.put("vod_year", year);
|
||||||
|
vodList.put("vod_area", area);
|
||||||
|
vodList.put("vod_actor", actor);
|
||||||
|
vodList.put("vod_director", director);
|
||||||
|
vodList.put("vod_content", desc);
|
||||||
|
Vod.VodPlayBuilder builder = new Vod.VodPlayBuilder();
|
||||||
|
List<String> shareLinks = new ArrayList<>();
|
||||||
|
getVodDetail(doc, builder, "1");
|
||||||
|
|
||||||
|
//多季剧集处理
|
||||||
|
Elements sources = doc.select(".post-page-numbers");
|
||||||
|
if (!sources.isEmpty()) {
|
||||||
|
for (Element source : sources) {
|
||||||
|
if (!source.select("a").isEmpty()) {
|
||||||
|
|
||||||
|
String Purl = source.select("a").attr("href");
|
||||||
|
Document docs = Jsoup.parse(OkHttp.string(Purl, getHeaders(Purl)));
|
||||||
|
getVodDetail(docs, builder, source.text());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Elements clouds = doc.select("p > a");
|
||||||
|
if (!clouds.isEmpty()) {
|
||||||
|
for (Element cloud : clouds) {
|
||||||
|
String cloudUrl = cloud.attr("href");
|
||||||
|
if (!Util.findByRegex(Util.patternQuark, cloudUrl, 0).isBlank() || !Util.findByRegex(Util.patternUC, cloudUrl, 0).isBlank()) {
|
||||||
|
shareLinks.add(cloudUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String quarkNames = "";
|
||||||
|
String quarkUrls = "";
|
||||||
|
if (!shareLinks.isEmpty()) {
|
||||||
|
quarkUrls = super.detailContentVodPlayUrl(shareLinks);
|
||||||
|
quarkNames = super.detailContentVodPlayFrom(shareLinks);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Vod.VodPlayBuilder.BuildResult buildResult = builder.build();
|
||||||
|
vodList.put("vod_play_from", buildResult.vodPlayFrom + "$$$" + quarkNames);
|
||||||
|
vodList.put("vod_play_url", buildResult.vodPlayUrl + "$$$" + quarkUrls);
|
||||||
|
|
||||||
|
JSONArray list = new JSONArray();
|
||||||
|
list.put(vodList);
|
||||||
|
result.put("list", list);
|
||||||
|
return result.toString();
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
SpiderDebug.log(e);
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private void getVodDetail(Document doc, Vod.VodPlayBuilder builder, String index) throws JSONException {
|
||||||
|
Elements allScript = doc.select(".wp-playlist-script");
|
||||||
|
String sourceName = "第" + index + "季";
|
||||||
|
for (Element element : allScript) {
|
||||||
|
String scContent = element.html().trim();
|
||||||
|
int start = scContent.indexOf('{');
|
||||||
|
int end = scContent.lastIndexOf('}') + 1;
|
||||||
|
String json = scContent.substring(start, end);
|
||||||
|
JSONObject UJson = new JSONObject(json);
|
||||||
|
JSONArray Track = UJson.getJSONArray("tracks");
|
||||||
|
List<Vod.VodPlayBuilder.PlayUrl> list = new ArrayList<>();
|
||||||
|
for (int k = 0; k < Track.length(); k++) {
|
||||||
|
JSONObject src = Track.getJSONObject(k);
|
||||||
|
String adk = src.getString("src0");
|
||||||
|
String vodName = src.getString("caption");
|
||||||
|
String pzm = getPlayUrl(adk);
|
||||||
|
Vod.VodPlayBuilder.PlayUrl playUrl = new Vod.VodPlayBuilder.PlayUrl();
|
||||||
|
playUrl.name = vodName;
|
||||||
|
playUrl.url = ProxyVideo.buildCommonProxyUrl(pzm, Util.webHeaders(siteUrl));
|
||||||
|
list.add(playUrl);
|
||||||
|
}
|
||||||
|
builder.append(sourceName, list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPlayUrl(String source) {
|
||||||
|
if (source.endsWith("m3u8")) {
|
||||||
|
return source;
|
||||||
|
} else if (source.startsWith("https")) {
|
||||||
|
return source;
|
||||||
|
} else {
|
||||||
|
return "https://v.ddys.pro" + source;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取视频播放信息
|
||||||
|
*
|
||||||
|
* @param flag 播放源
|
||||||
|
* @param id 视频id
|
||||||
|
* @param vipFlags 所有可能需要vip解析的源
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String playerContent(String flag, String id, List<String> vipFlags) throws Exception {
|
||||||
|
if (flag.contains("quark")||flag.contains("uc")) {
|
||||||
|
return super.playerContent(flag, id, vipFlags);
|
||||||
|
} else {
|
||||||
|
return Result.get().url(ProxyVideo.buildCommonProxyUrl(id, Util.webHeaders(siteUrl))).string();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String searchContent(String key, boolean quick) {
|
||||||
|
|
||||||
|
String url = siteUrl + "?s=" + URLEncoder.encode(key) + "&post_type=post";
|
||||||
|
Document doc = Jsoup.parse(OkHttp.string(url, getHeaders(url)));
|
||||||
|
List<Vod> vods = new ArrayList<>();
|
||||||
|
Elements elements = doc.select("h2.post-title > a");
|
||||||
|
for (int i = 0; i < elements.size(); i++) {
|
||||||
|
String id = elements.get(i).attr("href");
|
||||||
|
String name = elements.get(i).text();
|
||||||
|
vods.add(new Vod(id, name, ""));
|
||||||
|
}
|
||||||
|
return Result.string(vods);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,180 @@
|
||||||
|
package com.github.catvod.spider
|
||||||
|
|
||||||
|
import com.github.catvod.bean.Class
|
||||||
|
import com.github.catvod.bean.Result
|
||||||
|
import com.github.catvod.bean.Vod
|
||||||
|
import com.github.catvod.bean.Vod.VodPlayBuilder
|
||||||
|
import com.github.catvod.net.OkHttp
|
||||||
|
import com.github.catvod.utils.ProxyVideo
|
||||||
|
import com.github.catvod.utils.Util
|
||||||
|
import org.jsoup.Jsoup
|
||||||
|
import org.jsoup.nodes.Document
|
||||||
|
import org.jsoup.select.Elements
|
||||||
|
import java.net.URLEncoder
|
||||||
|
|
||||||
|
class DiDa : Cloud() {
|
||||||
|
private val headers: HashMap<String?, String?>
|
||||||
|
get() {
|
||||||
|
val headers = java.util.HashMap<String?, String?>()
|
||||||
|
headers.put("User-Agent", Util.CHROME)
|
||||||
|
return headers
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseVodFromDoc(doc: Document): MutableList<Vod?> {
|
||||||
|
val list: MutableList<Vod?> = ArrayList<Vod?>()
|
||||||
|
for (element in doc.select("div.myui-vodlist__box")) {
|
||||||
|
var pic = element.selectFirst("img")?.attr("src")
|
||||||
|
if (pic.isNullOrBlank()) {
|
||||||
|
pic = element.selectFirst("a")?.attr("data-original")
|
||||||
|
}
|
||||||
|
val url = element.selectFirst("a")?.attr("href")
|
||||||
|
val name = element.select("h4 > a").text()
|
||||||
|
|
||||||
|
|
||||||
|
list.add(Vod(url, name, pic))
|
||||||
|
|
||||||
|
}
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(Exception::class)
|
||||||
|
override fun homeContent(filter: Boolean): String? {
|
||||||
|
|
||||||
|
val doc = Jsoup.parse(
|
||||||
|
OkHttp.string(
|
||||||
|
siteUrl, this.headers
|
||||||
|
)
|
||||||
|
)
|
||||||
|
var classes: List<Class?>
|
||||||
|
val menuList = doc.select("ul.nav-menu > li > a")
|
||||||
|
classes = menuList.map {
|
||||||
|
val url = it.attr("href")
|
||||||
|
val title = it.text()
|
||||||
|
Class(url, title)
|
||||||
|
}
|
||||||
|
|
||||||
|
val list: MutableList<Vod?> = parseVodFromDoc(doc)
|
||||||
|
return Result.string(classes, list)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(Exception::class)
|
||||||
|
override fun categoryContent(
|
||||||
|
tid: String, pg: String, filter: Boolean, extend: java.util.HashMap<String?, String?>?
|
||||||
|
): String? {
|
||||||
|
|
||||||
|
val type = tid.replace("/type/", "").replace(".html", "")
|
||||||
|
val target: String = siteUrl + "/show/${type}--------${pg}---.html"
|
||||||
|
|
||||||
|
val doc = Jsoup.parse(OkHttp.string(target, this.headers))
|
||||||
|
val list = parseVodFromDoc(doc)
|
||||||
|
val total = (pg.toInt() + 1) * 24
|
||||||
|
return Result.get().vod(list).page(pg.toInt(), pg.toInt() + 1, 48, total).string()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(Exception::class)
|
||||||
|
override fun detailContent(ids: MutableList<String?>): String? {
|
||||||
|
val doc = Jsoup.parse(
|
||||||
|
OkHttp.string(
|
||||||
|
siteUrl + ids.get(0), this.headers
|
||||||
|
)
|
||||||
|
)
|
||||||
|
val name = doc.select("h1").text()
|
||||||
|
val img = doc.select("a.myui-vodlist__thumb > img")
|
||||||
|
val pic = img.attr("src") ?: img.attr("data-original")
|
||||||
|
|
||||||
|
val desc = doc.select("p.data").text()
|
||||||
|
val year = Util.findByRegex("年份:(.*)又名", desc, 1).trim()
|
||||||
|
val area = Util.findByRegex("地区:(.*)语言", desc, 1).trim()
|
||||||
|
val actor = Util.findByRegex("主演:(.*)导演", desc, 1).trim()
|
||||||
|
|
||||||
|
val builder = VodPlayBuilder()
|
||||||
|
val playFromTab = doc.selectFirst("div.myui-panel__head > ul.nav ")
|
||||||
|
val playFromEles = playFromTab?.select("ul > li > a") ?: Elements()
|
||||||
|
for (ele in playFromEles) {
|
||||||
|
val playUrlList = mutableListOf<Vod.VodPlayBuilder.PlayUrl>()
|
||||||
|
|
||||||
|
val id = ele.attr("href")
|
||||||
|
val playFrom = ele.text()
|
||||||
|
if (playFrom.contains("网盘")) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
val playElement = doc.select("$id")
|
||||||
|
for (element in playElement.select("ul > li > a")) {
|
||||||
|
val playUrl = VodPlayBuilder.PlayUrl()
|
||||||
|
playUrl.url = element.attr("href")
|
||||||
|
playUrl.name = element.text()
|
||||||
|
playUrlList.add(playUrl)
|
||||||
|
|
||||||
|
}
|
||||||
|
builder.append(playFrom, playUrlList)
|
||||||
|
}
|
||||||
|
var panFrom = ""
|
||||||
|
var panURl = ""
|
||||||
|
|
||||||
|
for (element in doc.select(" div.myui-panel_bd.clearfix > p > a")) {
|
||||||
|
if (element.attr("href").matches(Util.patternQuark.toRegex())) {
|
||||||
|
panFrom = super.detailContentVodPlayFrom(listOf<String>(element.attr("href")))
|
||||||
|
panURl = super.detailContentVodPlayUrl(listOf<String>(element.attr("href")))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
val result = builder.build()
|
||||||
|
val vod = Vod()
|
||||||
|
vod.setVodId(ids[0])
|
||||||
|
vod.setVodPic(ProxyVideo.buildCommonProxyUrl(pic, Util.webHeaders(pic)))
|
||||||
|
vod.setVodYear(year)
|
||||||
|
vod.setVodActor(actor)
|
||||||
|
vod.setVodArea(area)
|
||||||
|
vod.setVodName(name)
|
||||||
|
vod.setVodPlayFrom(result.vodPlayFrom + "$$$" + panFrom)
|
||||||
|
vod.setVodPlayUrl(result.vodPlayUrl + "$$$" + panURl)
|
||||||
|
return Result.string(vod)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(Exception::class)
|
||||||
|
override fun searchContent(key: String?, quick: Boolean): String? {
|
||||||
|
val doc = Jsoup.parse(
|
||||||
|
OkHttp.string(
|
||||||
|
searchUrl + URLEncoder.encode(key), this.headers
|
||||||
|
)
|
||||||
|
)
|
||||||
|
val list: MutableList<Vod?> = ArrayList<Vod?>()
|
||||||
|
for (element in doc.select("a.cover-link")) {
|
||||||
|
val pic = element.select("img").attr("data-src")
|
||||||
|
val url = element.attr("href")
|
||||||
|
val name = element.select("img").attr("alt")
|
||||||
|
val id: String? = url.split("/".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()[2]
|
||||||
|
|
||||||
|
list.add(Vod(id, name, ProxyVideo.buildCommonProxyUrl(pic, Util.webHeaders(pic))))
|
||||||
|
}
|
||||||
|
return Result.string(list)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(Exception::class)
|
||||||
|
override fun playerContent(flag: String?, id: String?, vipFlags: MutableList<String?>?): String? {
|
||||||
|
if (flag == null) {
|
||||||
|
return Result.get().url("").header(this.headers).string()
|
||||||
|
} else if (flag.contains("quark")) {
|
||||||
|
super.playerContent(flag, id, vipFlags)
|
||||||
|
} else {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return Result.get().url(id).header(this.headers).string()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val siteUrl = "https://www.didahd.pro"
|
||||||
|
|
||||||
|
private val searchUrl: String = siteUrl + "/search?q="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,151 @@
|
||||||
|
package com.github.catvod.spider;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import com.github.catvod.bean.Class;
|
||||||
|
import com.github.catvod.bean.Result;
|
||||||
|
import com.github.catvod.bean.Vod;
|
||||||
|
import com.github.catvod.net.OkHttp;
|
||||||
|
import com.github.catvod.utils.Util;
|
||||||
|
import org.jsoup.Jsoup;
|
||||||
|
import org.jsoup.nodes.Document;
|
||||||
|
import org.jsoup.nodes.Element;
|
||||||
|
import org.jsoup.select.Elements;
|
||||||
|
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 电影云集
|
||||||
|
*
|
||||||
|
* @author lushunming
|
||||||
|
* @createdate 2024-12-03
|
||||||
|
*/
|
||||||
|
public class DianYingYunJi extends Cloud {
|
||||||
|
|
||||||
|
private final String siteUrl = "https://dyyjpro.com";
|
||||||
|
|
||||||
|
|
||||||
|
private Map<String, String> getHeader() {
|
||||||
|
Map<String, String> header = new HashMap<>();
|
||||||
|
header.put("User-Agent", Util.CHROME);
|
||||||
|
return header;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, String> getHeaderWithCookie() {
|
||||||
|
Map<String, String> header = new HashMap<>();
|
||||||
|
header.put("User-Agent", Util.CHROME);
|
||||||
|
header.put("cookie", "esc_search_captcha=1; result=43");
|
||||||
|
return header;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(Context context, String extend) throws Exception {
|
||||||
|
|
||||||
|
super.init(context, extend);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String homeContent(boolean filter) {
|
||||||
|
List<Class> classes = new ArrayList<>();
|
||||||
|
Document doc = Jsoup.parse(OkHttp.string(siteUrl, getHeader()));
|
||||||
|
Elements elements = doc.select(" #header-navbar > li.menu-item > a");
|
||||||
|
for (Element e : elements) {
|
||||||
|
String url = e.attr("href");
|
||||||
|
String name = e.text();
|
||||||
|
if (url.contains(siteUrl)) {
|
||||||
|
classes.add(new Class(url, name));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.string(classes, parseVodListFromDoc(doc));
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Vod> parseVodListFromDoc(Document doc) {
|
||||||
|
List<Vod> list = new ArrayList<>();
|
||||||
|
Elements elements = doc.select(" article.post-item");
|
||||||
|
for (Element e : elements) {
|
||||||
|
String vodId = e.selectFirst("h2.entry-title > a").attr("href");
|
||||||
|
String vodPic = e.selectFirst(" div.entry-media > a").attr("data-bg");
|
||||||
|
if (!vodPic.startsWith("http")) {
|
||||||
|
vodPic = siteUrl + vodPic;
|
||||||
|
}
|
||||||
|
String vodName = e.selectFirst("h2.entry-title > a").text();
|
||||||
|
String vodRemarks = "";
|
||||||
|
list.add(new Vod(vodId, vodName, vodPic, vodRemarks));
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String categoryContent(String tid, String pg, boolean filter, HashMap<String, String> extend) {
|
||||||
|
|
||||||
|
Document doc = Jsoup.parse(OkHttp.string(String.format("%s/page/%s", tid, pg), getHeader()));
|
||||||
|
List<Vod> list = parseVodListFromDoc(doc);
|
||||||
|
int total = (Integer.parseInt(pg) + 1) * 19;
|
||||||
|
return Result.get().vod(list).page(Integer.parseInt(pg), Integer.parseInt(pg) + 1, 19, total).string();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String detailContent(List<String> ids) throws Exception {
|
||||||
|
String vodId = ids.get(0);
|
||||||
|
Document doc = Jsoup.parse(OkHttp.string(vodId, getHeader()));
|
||||||
|
|
||||||
|
Vod item = new Vod();
|
||||||
|
item.setVodId(vodId);
|
||||||
|
item.setVodName(doc.selectFirst(" h1.post-title").text());
|
||||||
|
item.setVodPic(doc.selectFirst("article.post-content img").attr("src"));
|
||||||
|
String html= doc.select("article.post-content > p").text();
|
||||||
|
item.setVodDirector(getStrByRegex(Pattern.compile("导演:(.*?)编剧:"), html));
|
||||||
|
item.setVodArea(getStrByRegex(Pattern.compile("地区:(.*?)语言:"), html));
|
||||||
|
item.setVodActor(getStrByRegex(Pattern.compile("主演:(.*?)类型:"), html));
|
||||||
|
item.setVodYear(getStrByRegex(Pattern.compile("上映日期:(.*?)片长:"), html));
|
||||||
|
item.setVodRemarks("");
|
||||||
|
item.setVodContent(getStrByRegex(Pattern.compile("剧情简介(.*?)获奖情况"), html));
|
||||||
|
|
||||||
|
List<String> shareLinks = new ArrayList<>();
|
||||||
|
|
||||||
|
for (Element element : doc.select("article.post-content p a")) {
|
||||||
|
if (element.attr("href").matches(Util.patternQuark)) {
|
||||||
|
shareLinks.add(element.attr("href").trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
item.setTypeName(doc.selectFirst(" span.meta-cat-dot").text());
|
||||||
|
|
||||||
|
item.setVodPlayUrl(super.detailContentVodPlayUrl(shareLinks));
|
||||||
|
item.setVodPlayFrom(super.detailContentVodPlayFrom(shareLinks));
|
||||||
|
|
||||||
|
return Result.string(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getStrByRegex(Pattern pattern, String str) {
|
||||||
|
Matcher matcher = pattern.matcher(str);
|
||||||
|
if (matcher.find()) return matcher.group(1).trim();
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String searchContent(String key, boolean quick) throws Exception {
|
||||||
|
return searchContent(key, "1");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String searchContent(String key, boolean quick, String pg) throws Exception {
|
||||||
|
return searchContent(key, pg);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String searchContent(String key, String pg) {
|
||||||
|
String searchURL = siteUrl + String.format("?cat=&s=%s", URLEncoder.encode(key));
|
||||||
|
String html = OkHttp.string(searchURL, getHeaderWithCookie());
|
||||||
|
Document doc = Jsoup.parse(html);
|
||||||
|
|
||||||
|
return Result.string(parseVodListFromDoc(doc));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,154 @@
|
||||||
|
package com.github.catvod.spider;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import com.github.catvod.bean.Class;
|
||||||
|
import com.github.catvod.bean.Result;
|
||||||
|
import com.github.catvod.bean.Vod;
|
||||||
|
import com.github.catvod.net.OkHttp;
|
||||||
|
import com.github.catvod.utils.Json;
|
||||||
|
import com.github.catvod.utils.Util;
|
||||||
|
import com.google.gson.JsonArray;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import org.jsoup.Jsoup;
|
||||||
|
import org.jsoup.nodes.Document;
|
||||||
|
import org.jsoup.nodes.Element;
|
||||||
|
import org.jsoup.select.Elements;
|
||||||
|
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author zhixc
|
||||||
|
*/
|
||||||
|
public class DuoDuo extends Cloud {
|
||||||
|
|
||||||
|
private String siteUrl = "https://tv.yydsys.top/";
|
||||||
|
private final Pattern regexCategory = Pattern.compile("index.php/vod/type/id/(\\w+).html");
|
||||||
|
private final Pattern regexPageTotal = Pattern.compile("\\$\\(\"\\.mac_total\"\\)\\.text\\('(\\d+)'\\);");
|
||||||
|
|
||||||
|
private Map<String, String> getHeader() {
|
||||||
|
Map<String, String> header = new HashMap<>();
|
||||||
|
header.put("User-Agent", Util.CHROME);
|
||||||
|
return header;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(Context context, String extend) throws Exception {
|
||||||
|
// JsonObject ext = Json.safeObject(extend);
|
||||||
|
super.init(context, extend);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String homeContent(boolean filter) {
|
||||||
|
List<Class> classes = new ArrayList<>();
|
||||||
|
Document doc = Jsoup.parse(OkHttp.string(siteUrl, getHeader()));
|
||||||
|
Elements elements = doc.select(".nav-link");
|
||||||
|
for (Element e : elements) {
|
||||||
|
Matcher mather = regexCategory.matcher(e.attr("href"));
|
||||||
|
if (mather.find()) {
|
||||||
|
classes.add(new Class(mather.group(1), e.text().trim()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Result.string(classes, parseVodListFromDoc(doc));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String categoryContent(String tid, String pg, boolean filter, HashMap<String, String> extend) {
|
||||||
|
String[] urlParams = new String[]{tid, "", "", "", "", "", "", "", pg, "", "", ""};
|
||||||
|
if (extend != null && extend.size() > 0) {
|
||||||
|
for (String key : extend.keySet()) {
|
||||||
|
urlParams[Integer.parseInt(key)] = extend.get(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Document doc = Jsoup.parse(OkHttp.string(String.format("%s/index.php/vod/show/id/%s/page/%s.html", siteUrl, tid, pg), getHeader()));
|
||||||
|
int page = Integer.parseInt(pg), limit = 72, total = 0;
|
||||||
|
Matcher matcher = regexPageTotal.matcher(doc.html());
|
||||||
|
if (matcher.find()) total = Integer.parseInt(matcher.group(1));
|
||||||
|
int count = total <= limit ? 1 : ((int) Math.ceil(total / (double) limit));
|
||||||
|
return Result.get().vod(parseVodListFromDoc(doc)).page(page, count, limit, total).string();
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Vod> parseVodListFromDoc(Document doc) {
|
||||||
|
List<Vod> list = new ArrayList<>();
|
||||||
|
Elements elements = doc.select(".module-item");
|
||||||
|
for (Element e : elements) {
|
||||||
|
String vodId = e.selectFirst(".video-name a").attr("href");
|
||||||
|
String vodPic = e.selectFirst(".module-item-pic > img").attr("data-src");
|
||||||
|
String vodName = e.selectFirst(".video-name").text();
|
||||||
|
String vodRemarks = e.selectFirst(".module-item-text").text();
|
||||||
|
list.add(new Vod(vodId, vodName, vodPic, vodRemarks));
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String detailContent(List<String> ids) throws Exception {
|
||||||
|
String vodId = ids.get(0);
|
||||||
|
Document doc = Jsoup.parse(OkHttp.string(siteUrl + vodId, getHeader()));
|
||||||
|
|
||||||
|
Vod item = new Vod();
|
||||||
|
item.setVodId(vodId);
|
||||||
|
item.setVodName(doc.selectFirst(".video-info-header > .page-title").text());
|
||||||
|
item.setVodPic(doc.selectFirst(".module-item-pic img").attr("data-src"));
|
||||||
|
item.setVodArea(doc.select(".video-info-header a.tag-link").last().text());
|
||||||
|
item.setTypeName(String.join(",", doc.select(".video-info-header div.tag-link a").eachText()));
|
||||||
|
|
||||||
|
List<String> shareLinks = doc.select(".module-row-text").eachAttr("data-clipboard-text");
|
||||||
|
for (int i = 0; i < shareLinks.size(); i++) {
|
||||||
|
shareLinks.set(i, shareLinks.get(i).trim());
|
||||||
|
//String detailContent = super.detailContent(List.of(shareLinks.get(i)));
|
||||||
|
}
|
||||||
|
item.setVodPlayUrl(super.detailContentVodPlayUrl(shareLinks));
|
||||||
|
item.setVodPlayFrom(super.detailContentVodPlayFrom(shareLinks));
|
||||||
|
|
||||||
|
Elements elements = doc.select(".video-info-item");
|
||||||
|
for (Element e : elements) {
|
||||||
|
String title = e.previousElementSibling().text();
|
||||||
|
if (title.contains("导演")) {
|
||||||
|
item.setVodDirector(String.join(",", e.select("a").eachText()));
|
||||||
|
} else if (title.contains("主演")) {
|
||||||
|
item.setVodActor(String.join(",", e.select("a").eachText()));
|
||||||
|
} else if (title.contains("年代")) {
|
||||||
|
item.setVodYear(e.selectFirst("a").text().trim());
|
||||||
|
} else if (title.contains("备注")) {
|
||||||
|
item.setVodRemarks(e.text().trim());
|
||||||
|
} else if (title.contains("剧情")) {
|
||||||
|
item.setVodContent(e.selectFirst(".sqjj_a").text().replace("[收起部分]", "").trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.string(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String searchContent(String key, boolean quick) throws Exception {
|
||||||
|
return searchContent(key, "1");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String searchContent(String key, boolean quick, String pg) throws Exception {
|
||||||
|
return searchContent(key, pg);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private String searchContent(String key, String pg) {
|
||||||
|
String searchURL = siteUrl + String.format("/index.php/vod/search/page/%s/wd/%s.html", pg,URLEncoder.encode(key));
|
||||||
|
String html = OkHttp.string(searchURL, getHeader());
|
||||||
|
Elements items = Jsoup.parse(html).select(".module-search-item");
|
||||||
|
List<Vod> list = new ArrayList<>();
|
||||||
|
for (Element item : items) {
|
||||||
|
String vodId = item.select(".video-serial").attr("href");
|
||||||
|
String name = item.select(".video-serial").attr("title");
|
||||||
|
String pic = item.select(".module-item-pic > img").attr("data-src");
|
||||||
|
String remark = item.select(".video-tag-icon").text();
|
||||||
|
list.add(new Vod(vodId, name, pic, remark));
|
||||||
|
}
|
||||||
|
return Result.string(list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,184 @@
|
||||||
|
package com.github.catvod.spider
|
||||||
|
|
||||||
|
|
||||||
|
import com.github.catvod.bean.Class
|
||||||
|
import com.github.catvod.bean.Result
|
||||||
|
import com.github.catvod.bean.Vod
|
||||||
|
|
||||||
|
import com.github.catvod.crawler.Spider
|
||||||
|
import com.github.catvod.crawler.SpiderDebug
|
||||||
|
import com.github.catvod.net.OkHttp
|
||||||
|
import com.github.catvod.utils.Json
|
||||||
|
import com.github.catvod.utils.Util
|
||||||
|
import com.google.gson.JsonArray
|
||||||
|
import org.apache.commons.lang3.time.DateFormatUtils
|
||||||
|
import org.apache.commons.lang3.time.DateUtils
|
||||||
|
import org.jsoup.Jsoup
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class Glod : Spider() {
|
||||||
|
private val host = Util.base64Decode("aHR0cHM6Ly93d3cuY2Zrajg2LmNvbS8=")
|
||||||
|
|
||||||
|
private val epUrl = "/api/mw-movie/anonymous/v1/video/episode/url?id=%s&nid=%s"
|
||||||
|
|
||||||
|
private val deviceId = UUID.randomUUID().toString();
|
||||||
|
|
||||||
|
private val classList = listOf(Class("1", "电影"), Class("2", "电视剧"), Class("4", "动漫"), Class("3", "综艺"))
|
||||||
|
|
||||||
|
override fun homeContent(filter: Boolean): String {
|
||||||
|
val string = OkHttp.string(host, Util.webHeaders("https://www.bing.com"))
|
||||||
|
val vodList = parseFromJson(string, "home")
|
||||||
|
// val vodList = parseVodList(string)
|
||||||
|
return Result.string(classList, vodList)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun detailContent(ids: MutableList<String>): String {
|
||||||
|
val url = host + ids[0]
|
||||||
|
val string = OkHttp.string(url, Util.webHeaders(host))
|
||||||
|
val parse = Jsoup.parse(string)
|
||||||
|
val name = parse.select("h1.title").text()
|
||||||
|
val img = parse.select("div[class^=detail__CardImg] img").attr("src")
|
||||||
|
val tag = parse.select("div.tags > a.tag").eachText().joinToString(" ")
|
||||||
|
val vod = Vod(ids[0], name, img, tag)
|
||||||
|
val director = parse.select("div.director")
|
||||||
|
val d = director[0].select("a").text()
|
||||||
|
val actor = director[1].select("a").eachText().joinToString(" ")
|
||||||
|
vod.setVodActor(actor)
|
||||||
|
vod.setVodDirector(d)
|
||||||
|
val desc = parse.select("div.intro div.wrapper_more_text").text()
|
||||||
|
vod.vodContent = desc
|
||||||
|
|
||||||
|
val linkList = parse.select("div.listitem > a")
|
||||||
|
|
||||||
|
val playUrlList = mutableListOf<Vod.VodPlayBuilder.PlayUrl>()
|
||||||
|
for (element in linkList) {
|
||||||
|
val u = element.attr("href")
|
||||||
|
val n = element.text()
|
||||||
|
playUrlList.add(Vod.VodPlayBuilder.PlayUrl().also {
|
||||||
|
it.name = n
|
||||||
|
it.url = u
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
val buildResult = Vod.VodPlayBuilder().append("glod", playUrlList).build()
|
||||||
|
|
||||||
|
val time = parse.select("div.item:contains(上映时间)").select(".item-top").text()
|
||||||
|
vod.setVodYear(DateFormatUtils.format(DateUtils.parseDate(time, "yyyy-MM-dd"), "yyyy"))
|
||||||
|
vod.setVodPlayFrom(buildResult.vodPlayFrom)
|
||||||
|
vod.vodPlayUrl = buildResult.vodPlayUrl
|
||||||
|
return Result.string(vod)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求头
|
||||||
|
* t 时间戳
|
||||||
|
* sign 签名
|
||||||
|
* deviceId
|
||||||
|
* authorization 空的
|
||||||
|
* 还有cookie
|
||||||
|
*/
|
||||||
|
override fun playerContent(flag: String, id: String, vipFlags: MutableList<String>): String {
|
||||||
|
val list = id.split("/")
|
||||||
|
val i = list[3]
|
||||||
|
val nid = list[5]
|
||||||
|
val webHeaders = Util.webHeaders(host)
|
||||||
|
val time = Date().time.toString()
|
||||||
|
val sign = Util.sha1Hex(
|
||||||
|
Util.MD5("id=${i}&nid=${nid}&key=cb808529bae6b6be45ecfab29a4889bc&t=${time}")
|
||||||
|
)
|
||||||
|
webHeaders["t"] = time
|
||||||
|
webHeaders["deviceId"] = deviceId
|
||||||
|
webHeaders["Sign"] = sign
|
||||||
|
|
||||||
|
val string = OkHttp.string(host + String.format(epUrl, i, nid), webHeaders)
|
||||||
|
val parse = Json.parse(string).asJsonObject
|
||||||
|
if (parse.get("code").asInt != 200) {
|
||||||
|
SpiderDebug.log("glod 获取播放链接失败:$string")
|
||||||
|
return Result.error("获取播放链接失败")
|
||||||
|
}
|
||||||
|
val url = parse.get("data").asJsonObject.get("playUrl").asString
|
||||||
|
return Result.get().url(url).string()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun categoryContent(tid: String, pg: String, filter: Boolean, extend: HashMap<String, String>): String {
|
||||||
|
val url = "$host/type/$tid"
|
||||||
|
val string = OkHttp.string(url, Util.webHeaders(host))
|
||||||
|
val vodList = parseFromJson(string, "cate")
|
||||||
|
return Result.string(classList, vodList)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun searchContent(key: String, quick: Boolean): String {
|
||||||
|
val string = OkHttp.string("${host}vod/search/$key", Util.webHeaders(host))
|
||||||
|
val vodList = parseFromJson(string, "search")
|
||||||
|
return Result.string(vodList)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseFromJson(string: String, type: String): List<Vod> {
|
||||||
|
val vodList = mutableListOf<Vod>()
|
||||||
|
val parse = Jsoup.parse(string)
|
||||||
|
val select = parse.select("script")
|
||||||
|
val data = select.find {
|
||||||
|
it.html().contains("操作成功")
|
||||||
|
}
|
||||||
|
if (data == null) {
|
||||||
|
SpiderDebug.log("glod 找不到json")
|
||||||
|
return vodList
|
||||||
|
}
|
||||||
|
val json = data.html().replace("self.__next_f.push(", "").replace(")", "")
|
||||||
|
|
||||||
|
val gson = Json.parse(json).asJsonArray.get(1).asString.replace("6:", "")
|
||||||
|
val resp = Json.parse(gson).asJsonArray.get(3).asJsonObject
|
||||||
|
if (type == "home") {
|
||||||
|
val element = resp.get("children").asJsonArray.get(3).asJsonObject.get("data").asJsonObject.get("data")
|
||||||
|
var vList = element.asJsonObject.get("homeNewMoviePageData").asJsonObject.get("list").asJsonArray
|
||||||
|
getVodList(vList, vodList)
|
||||||
|
vList = element.asJsonObject.get("homeBroadcastPageData").asJsonObject.get("list").asJsonArray
|
||||||
|
getVodList(vList, vodList)
|
||||||
|
vList = element.asJsonObject.get("homeManagerPageData").asJsonObject.get("list").asJsonArray
|
||||||
|
getVodList(vList, vodList)
|
||||||
|
vList = element.asJsonObject.get("newestTvPageData").asJsonObject.get("list").asJsonArray
|
||||||
|
getVodList(vList, vodList)
|
||||||
|
vList = element.asJsonObject.get("newestCartoonPageData").asJsonObject.get("list").asJsonArray
|
||||||
|
getVodList(vList, vodList)
|
||||||
|
} else if (type == "cate") {
|
||||||
|
for (jsonElement in resp.get("children").asJsonArray.get(3).asJsonObject.get("data").asJsonArray) {
|
||||||
|
val objList = jsonElement.asJsonObject.get("vodList").asJsonObject.get("list").asJsonArray
|
||||||
|
getVodList(objList, vodList)
|
||||||
|
}
|
||||||
|
} else if (type == "search") {
|
||||||
|
val asJsonArray =
|
||||||
|
resp.get("data").asJsonObject.get("data").asJsonObject.get("result").asJsonObject.get("list").asJsonArray
|
||||||
|
getVodList(asJsonArray, vodList)
|
||||||
|
}
|
||||||
|
return vodList
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getVodList(
|
||||||
|
objList: JsonArray, vodList: MutableList<Vod>
|
||||||
|
) {
|
||||||
|
for (oj in objList) {
|
||||||
|
val obj = oj.asJsonObject
|
||||||
|
val v = Vod()
|
||||||
|
v.setVodId("/detail/" + obj.get("vodId").asString)
|
||||||
|
v.setVodName(obj.get("vodName").asString)
|
||||||
|
// v.setVodActor(obj.get("vodActor").asString)
|
||||||
|
v.setVodRemarks(obj.get("vodScore").asString)
|
||||||
|
v.setVodPic(obj.get("vodPic").asString)
|
||||||
|
vodList.add(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseVodList(string: String): MutableList<Vod> {
|
||||||
|
val parse = Jsoup.parse(string)
|
||||||
|
val list = parse.select("div.content-card")
|
||||||
|
val vodList = mutableListOf<Vod>()
|
||||||
|
for (element in list) {
|
||||||
|
val id = element.select("a").attr("href")
|
||||||
|
val title = element.select("div.info-title-box > div.title").text()
|
||||||
|
val score = element.select("div.bottom div[class^=score]").text()
|
||||||
|
val img = element.select("img").attr("srcset")
|
||||||
|
vodList.add(Vod(id, title, host + img, score))
|
||||||
|
}
|
||||||
|
return vodList
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,221 @@
|
||||||
|
package com.github.catvod.spider;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.util.Base64;
|
||||||
|
|
||||||
|
import com.github.catvod.bean.Class;
|
||||||
|
import com.github.catvod.bean.Result;
|
||||||
|
import com.github.catvod.bean.Vod;
|
||||||
|
import com.github.catvod.crawler.Spider;
|
||||||
|
import com.github.catvod.net.OkHttp;
|
||||||
|
import com.github.catvod.utils.Json;
|
||||||
|
import com.github.catvod.utils.Util;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.jsoup.Jsoup;
|
||||||
|
import org.jsoup.nodes.Document;
|
||||||
|
import org.jsoup.nodes.Element;
|
||||||
|
import org.jsoup.select.Elements;
|
||||||
|
|
||||||
|
import java.net.URLDecoder;
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
public class HkTv extends Spider {
|
||||||
|
|
||||||
|
private static String siteUrl = "http://www.tvyb03.com";
|
||||||
|
private static String cateUrl = siteUrl + "/vod/type/id/";
|
||||||
|
private static String detailUrl = siteUrl + "/vod/detail/id/";
|
||||||
|
private static String playUrl = siteUrl + "/vod/play/id/";
|
||||||
|
private static String searchUrl = siteUrl + "/vod/search.html?wd=";
|
||||||
|
|
||||||
|
private HashMap<String, String> getHeaders() {
|
||||||
|
HashMap<String, String> headers = new HashMap<>();
|
||||||
|
headers.put("User-Agent", Util.CHROME);
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(Context context, String extend) throws Exception {
|
||||||
|
super.init(context, extend);
|
||||||
|
Document doc = Jsoup.parse(OkHttp.string(extend));
|
||||||
|
|
||||||
|
|
||||||
|
if (StringUtils.isNoneBlank(doc.html())) {
|
||||||
|
String data = doc.select("ul > li > a").first().attr("href");
|
||||||
|
siteUrl = data;
|
||||||
|
}
|
||||||
|
cateUrl = siteUrl + "/vod/type/id/";
|
||||||
|
detailUrl = siteUrl + "/vod/detail/id/";
|
||||||
|
playUrl = siteUrl + "/vod/play/id/";
|
||||||
|
searchUrl = siteUrl + "/vod/search.html?wd=";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String homeContent(boolean filter) throws Exception {
|
||||||
|
List<Vod> list = new ArrayList<>();
|
||||||
|
List<Class> classes = new ArrayList<>();
|
||||||
|
String[] typeIdList = {"1", "2", "3", "4", "19"};
|
||||||
|
String[] typeNameList = {"电影", "电视剧", "综艺", "动漫", "短片"};
|
||||||
|
for (int i = 0; i < typeNameList.length; i++) {
|
||||||
|
classes.add(new Class(typeIdList[i], typeNameList[i]));
|
||||||
|
}
|
||||||
|
Document doc = Jsoup.parse(OkHttp.string(siteUrl, getHeaders()));
|
||||||
|
for (Element element : doc.select("a.myui-vodlist__thumb")) {
|
||||||
|
try {
|
||||||
|
String pic = element.attr("data-original");
|
||||||
|
String url = element.attr("href");
|
||||||
|
String name = element.attr("title");
|
||||||
|
if (!pic.startsWith("http")) {
|
||||||
|
pic = siteUrl + pic;
|
||||||
|
}
|
||||||
|
String id = url.split("/")[4];
|
||||||
|
list.add(new Vod(id, name, pic));
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Result.string(classes, list);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String categoryContent(String tid, String pg, boolean filter, HashMap<String, String> extend) throws Exception {
|
||||||
|
List<Vod> list = new ArrayList<>();
|
||||||
|
String target = cateUrl + tid + ".html";
|
||||||
|
if (!"1".equals(pg)) {
|
||||||
|
target = cateUrl + pg + "/page/" + tid + ".html";
|
||||||
|
}
|
||||||
|
Document doc = Jsoup.parse(OkHttp.string(target, getHeaders()));
|
||||||
|
for (Element element : doc.select("ul.myui-vodlist li a.myui-vodlist__thumb")) {
|
||||||
|
try {
|
||||||
|
String pic = element.attr("data-original");
|
||||||
|
String url = element.attr("href");
|
||||||
|
String name = element.attr("title");
|
||||||
|
if (!pic.startsWith("http")) {
|
||||||
|
pic = siteUrl + pic;
|
||||||
|
}
|
||||||
|
String id = url.split("/")[4];
|
||||||
|
list.add(new Vod(id, name, pic));
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Integer total = (Integer.parseInt(pg) + 1) * 20;
|
||||||
|
return Result.string(Integer.parseInt(pg), Integer.parseInt(pg) + 1, 20, total, list);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String detailContent(List<String> ids) throws Exception {
|
||||||
|
Document doc = Jsoup.parse(OkHttp.string(detailUrl.concat(ids.get(0)), getHeaders()));
|
||||||
|
String name = doc.select("h1.title").text();
|
||||||
|
String pic = doc.select("a.myui-vodlist__thumb.picture img").attr("data-original");
|
||||||
|
// 播放源
|
||||||
|
Elements tabs = doc.select("div.myui-panel__head.bottom-line.active.clearfix h3");
|
||||||
|
Elements list = doc.select("ul.myui-content__list");
|
||||||
|
String PlayFrom = "";
|
||||||
|
String PlayUrl = "";
|
||||||
|
for (int i = 1; i < tabs.size() - 1; i++) {
|
||||||
|
String tabName = tabs.get(i).text();
|
||||||
|
if (!"".equals(PlayFrom)) {
|
||||||
|
PlayFrom = PlayFrom + "$$$" + tabName;
|
||||||
|
} else {
|
||||||
|
PlayFrom = PlayFrom + tabName;
|
||||||
|
}
|
||||||
|
Elements li = list.get(i - 1).select("a");
|
||||||
|
String liUrl = "";
|
||||||
|
for (int i1 = 0; i1 < li.size(); i1++) {
|
||||||
|
if (!"".equals(liUrl)) {
|
||||||
|
liUrl = liUrl + "#" + li.get(i1).text() + "$" + li.get(i1).attr("href").replace("/vod/play/id/", "");
|
||||||
|
} else {
|
||||||
|
liUrl = liUrl + li.get(i1).text() + "$" + li.get(i1).attr("href").replace("/vod/play/id/", "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!"".equals(PlayUrl)) {
|
||||||
|
PlayUrl = PlayUrl + "$$$" + liUrl;
|
||||||
|
} else {
|
||||||
|
PlayUrl = PlayUrl + liUrl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Vod vod = new Vod();
|
||||||
|
vod.setVodId(ids.get(0));
|
||||||
|
vod.setVodPic(siteUrl + pic);
|
||||||
|
vod.setVodName(name);
|
||||||
|
vod.setVodPlayFrom(PlayFrom);
|
||||||
|
vod.setVodPlayUrl(PlayUrl);
|
||||||
|
return Result.string(vod);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
// 需要验证码
|
||||||
|
public String searchContent(String key, boolean quick) throws Exception {
|
||||||
|
List<Vod> list = new ArrayList<>();
|
||||||
|
Document doc = Jsoup.parse(OkHttp.string(searchUrl.concat(URLEncoder.encode(key)), getHeaders()));
|
||||||
|
for (Element element : doc.select("div.searchlist_img")) {
|
||||||
|
try {
|
||||||
|
String pic = element.select("a").attr("data-original");
|
||||||
|
String url = element.select("a").attr("href");
|
||||||
|
String name = element.select("a").attr("title");
|
||||||
|
if (!pic.startsWith("http")) {
|
||||||
|
pic = siteUrl + pic;
|
||||||
|
}
|
||||||
|
String id = url.replace("/video/", "").replace(".html", "-1-1.html");
|
||||||
|
list.add(new Vod(id, name, pic));
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Result.string(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String playerContent(String flag, String id, List<String> vipFlags) throws Exception {
|
||||||
|
String target = playUrl.concat(id);
|
||||||
|
Document doc = Jsoup.parse(OkHttp.string(target, getHeaders()));
|
||||||
|
String regex = "\"url\\\":\\\"(.*?)\\\",\\\"url_next\\\":";
|
||||||
|
|
||||||
|
Pattern pattern = Pattern.compile(regex);
|
||||||
|
Matcher matcher = pattern.matcher(doc.html());
|
||||||
|
String url = doc.html();
|
||||||
|
if (matcher.find()) {
|
||||||
|
String encryptedData = matcher.group(1);
|
||||||
|
String decodedString = new String(Base64.decode(encryptedData, Base64.DEFAULT));
|
||||||
|
url = decodeURL(decodedString);
|
||||||
|
}
|
||||||
|
return Result.get().url(url).header(getHeaders()).string();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String decodeURL(String encodedURL) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
int index = 0;
|
||||||
|
while (index < encodedURL.length()) {
|
||||||
|
if (encodedURL.charAt(index) == '%') {
|
||||||
|
if (index + 2 < encodedURL.length()) {
|
||||||
|
if (encodedURL.charAt(index + 1) == 'u') {
|
||||||
|
String unicodeStr = encodedURL.substring(index + 2, index + 6);
|
||||||
|
char unicodeChar = (char) Integer.parseInt(unicodeStr, 16);
|
||||||
|
sb.append(unicodeChar);
|
||||||
|
index += 6;
|
||||||
|
} else {
|
||||||
|
String hexStr = encodedURL.substring(index + 1, index + 3);
|
||||||
|
char hexChar = (char) Integer.parseInt(hexStr, 16);
|
||||||
|
sb.append(hexChar);
|
||||||
|
index += 3;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sb.append(encodedURL.charAt(index));
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sb.append(encodedURL.charAt(index));
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,210 @@
|
||||||
|
package com.github.catvod.spider;
|
||||||
|
|
||||||
|
import com.github.catvod.bean.Class;
|
||||||
|
import com.github.catvod.bean.Result;
|
||||||
|
import com.github.catvod.bean.Vod;
|
||||||
|
import com.github.catvod.crawler.Spider;
|
||||||
|
import com.github.catvod.net.OkHttp;
|
||||||
|
import com.github.catvod.utils.ProxyVideo;
|
||||||
|
import com.github.catvod.utils.Util;
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.JsonArray;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
|
||||||
|
import org.jsoup.Jsoup;
|
||||||
|
import org.jsoup.nodes.Document;
|
||||||
|
import org.jsoup.nodes.Element;
|
||||||
|
import org.jsoup.select.Elements;
|
||||||
|
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
public class Ikanbot extends Spider {
|
||||||
|
|
||||||
|
private static final String siteUrl = "https://v.ikanbot.com";
|
||||||
|
private static final String cateUrl = siteUrl + "/hot";
|
||||||
|
private static final String detailUrl = siteUrl + "/play/";
|
||||||
|
private static final String searchUrl = siteUrl + "/search?q=";
|
||||||
|
|
||||||
|
private HashMap<String, String> getHeaders() {
|
||||||
|
HashMap<String, String> headers = new HashMap<>();
|
||||||
|
headers.put("User-Agent", Util.CHROME);
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Vod> parseVods(Document doc) {
|
||||||
|
List<Vod> list = new ArrayList<>();
|
||||||
|
for (Element element : doc.select("a.item")) {
|
||||||
|
String pic = element.select("img").attr("data-src");
|
||||||
|
String url = element.attr("href");
|
||||||
|
String name = element.select("img").attr("alt");
|
||||||
|
String id = url.split("/")[2];
|
||||||
|
list.add(new Vod(id, name, ProxyVideo.buildCommonProxyUrl(pic, Util.webHeaders(pic))));
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String homeContent(boolean filter) throws Exception {
|
||||||
|
List<Class> classes = new ArrayList<>();
|
||||||
|
String[] typeIdList = {"/index-movie-热门", "/index-tv-热门", "/index-tv-国产剧", "/index-tv-韩剧"};
|
||||||
|
String[] typeNameList = {"热门电影", "热门剧集", "国产剧", "韩剧"};
|
||||||
|
for (int i = 0; i < typeNameList.length; i++) {
|
||||||
|
classes.add(new Class(typeIdList[i], typeNameList[i]));
|
||||||
|
}
|
||||||
|
Document doc = Jsoup.parse(OkHttp.string(siteUrl + "/billboard.html", getHeaders()));
|
||||||
|
List<Vod> list = new ArrayList<>();
|
||||||
|
for (Element element : doc.select("div.item-root")) {
|
||||||
|
String pic = element.select("img").attr("data-src");
|
||||||
|
String url = element.select("a").attr("href");
|
||||||
|
String name = element.select("img").attr("alt");
|
||||||
|
try {
|
||||||
|
String id = url.split("/")[2];
|
||||||
|
list.add(new Vod(id, name, ProxyVideo.buildCommonProxyUrl(pic, Util.webHeaders(pic))));
|
||||||
|
} catch (Exception e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Result.string(classes, list);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String categoryContent(String tid, String pg, boolean filter, HashMap<String, String> extend) throws Exception {
|
||||||
|
String target = cateUrl + tid;
|
||||||
|
if (!"1".equals(pg)) {
|
||||||
|
target = target + "-p-" + pg;
|
||||||
|
}
|
||||||
|
Document doc = Jsoup.parse(OkHttp.string(target.concat(".html"), getHeaders()));
|
||||||
|
List<Vod> list = parseVods(doc);
|
||||||
|
Integer total = (Integer.parseInt(pg) + 1) * 24;
|
||||||
|
return Result.get().vod(list).page(Integer.parseInt(pg), Integer.parseInt(pg) + 1, 24, total).string();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String detailContent(List<String> ids) throws Exception {
|
||||||
|
Document doc = Jsoup.parse(OkHttp.string(detailUrl.concat(ids.get(0)), getHeaders()));
|
||||||
|
String name = doc.select("h1").text();
|
||||||
|
String pic = doc.select("meta[property=og:image]").attr("content");
|
||||||
|
Elements desc = doc.select("div.detail > h3");
|
||||||
|
String year = desc.get(1).text();
|
||||||
|
String area = desc.get(2).text();
|
||||||
|
String actor = desc.get(3).text();
|
||||||
|
|
||||||
|
String current_id = doc.select("input#current_id").attr("value");
|
||||||
|
String e_token = doc.select("input#e_token").attr("value");
|
||||||
|
String mtype = doc.select("input#mtype").attr("value");
|
||||||
|
String tks = get_tks(current_id, e_token);
|
||||||
|
String url = siteUrl + "/api/getResN?videoId=" + ids.get(0) + "&mtype=" + mtype + " &token=" + tks;
|
||||||
|
String data = OkHttp.string(url, getHeaders());
|
||||||
|
Gson gson = new Gson();
|
||||||
|
JsonObject jsonObject = gson.fromJson(data, JsonObject.class);
|
||||||
|
JsonArray array = jsonObject.getAsJsonObject("data").getAsJsonArray("list");
|
||||||
|
String PlayFrom = "";
|
||||||
|
String PlayUrl = "";
|
||||||
|
for (JsonElement element : array) {
|
||||||
|
|
||||||
|
// 使用正则表达式匹配 "flag" 和 "url"
|
||||||
|
Pattern pattern = Pattern.compile("\\\"flag\\\":\\\"(.*?)\\\",\\\"url\\\":\\\"(.*?)\\\"");
|
||||||
|
Matcher matcher = pattern.matcher(String.valueOf(element.getAsJsonObject().get("resData")).replace("\\", ""));
|
||||||
|
String flag = "";
|
||||||
|
String liUrl = "";
|
||||||
|
// 提取匹配到的内容
|
||||||
|
if (matcher.find()) {
|
||||||
|
flag = matcher.group(1);
|
||||||
|
liUrl = matcher.group(2);
|
||||||
|
}
|
||||||
|
if (!"".equals(PlayFrom)) {
|
||||||
|
PlayFrom = PlayFrom + "$$$" + flag;
|
||||||
|
} else {
|
||||||
|
PlayFrom = PlayFrom + flag;
|
||||||
|
}
|
||||||
|
if (!"".equals(PlayUrl)) {
|
||||||
|
PlayUrl = PlayUrl + "$$$" + liUrl.replace("$" + flag, "");
|
||||||
|
} else {
|
||||||
|
PlayUrl = PlayUrl + liUrl.replace("$" + flag, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
// PlayUrl += String.valueOf(element.getAsJsonObject().get("resData")).replace("\\","").replace("\\\"","\"");
|
||||||
|
}
|
||||||
|
Vod vod = new Vod();
|
||||||
|
vod.setVodId(ids.get(0));
|
||||||
|
vod.setVodPic(ProxyVideo.buildCommonProxyUrl(pic, Util.webHeaders(pic)));
|
||||||
|
vod.setVodYear(year);
|
||||||
|
vod.setVodActor(actor);
|
||||||
|
vod.setVodArea(area);
|
||||||
|
vod.setVodName(name);
|
||||||
|
vod.setVodPlayFrom(PlayFrom);
|
||||||
|
vod.setVodPlayUrl(PlayUrl.replace("##", "#").replace("#$$$", "$$$"));
|
||||||
|
return Result.string(vod);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String searchContent(String key, boolean quick) throws Exception {
|
||||||
|
Document doc = Jsoup.parse(OkHttp.string(searchUrl.concat(URLEncoder.encode(key)), getHeaders()));
|
||||||
|
List<Vod> list = new ArrayList<>();
|
||||||
|
for (Element element : doc.select("a.cover-link")) {
|
||||||
|
String pic = element.select("img").attr("data-src");
|
||||||
|
String url = element.attr("href");
|
||||||
|
String name = element.select("img").attr("alt");
|
||||||
|
String id = url.split("/")[2];
|
||||||
|
|
||||||
|
list.add(new Vod(id, name, ProxyVideo.buildCommonProxyUrl(pic, Util.webHeaders(pic))));
|
||||||
|
}
|
||||||
|
return Result.string(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String playerContent(String flag, String id, List<String> vipFlags) throws Exception {
|
||||||
|
return Result.get().url(id).header(getHeaders()).string();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// function get_tks() {
|
||||||
|
// const _0xf07220 = _0xf746;
|
||||||
|
// let _0x35162d = document['getElementById'] ('current_id').value 'current_id'
|
||||||
|
// , _0xf25678 = document['getElementById'] ('e_token').value;
|
||||||
|
// 'e_token'
|
||||||
|
// if (!_0x35162d || !_0xf25678)
|
||||||
|
// return;
|
||||||
|
// let _0x3882a3 = _0x35162d['length'], _0x52a097 = _0x35162d['substring']
|
||||||
|
// (_0x3882a3 - 4, _0x3882a3)
|
||||||
|
// ,_0x2d9d1b = [];
|
||||||
|
// for (let _0x570711 = 0x0; _0x570711 < _0x52a097['length']; _0x570711++) {
|
||||||
|
// let _0x23e537 = parseInt(_0x52a097[_0x570711]), _0x48b93d = _0x23e537 % 0x3 + 0x1;
|
||||||
|
// _0x2d9d1b[_0x570711] = _0xf25678['substring'] (_0x48b93d, _0x48b93d + 0x8),
|
||||||
|
// _0xf25678 = _0xf25678['substring'] (_0x48b93d + 0x8, _0xf25678['length']);
|
||||||
|
// }
|
||||||
|
// v_tks = _0x2d9d1b['join'] ('');
|
||||||
|
// }
|
||||||
|
|
||||||
|
public String get_tks(String current_id, String e_token) {
|
||||||
|
// String current_id = "798347";
|
||||||
|
// String e_token = "mre0530ce88964487488y67d38c0a1uj7fd15cb8";
|
||||||
|
System.out.printf("current_id " + current_id);
|
||||||
|
System.out.printf("e_token " + e_token);
|
||||||
|
if ("".equals(current_id) || "".equals(e_token)) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
String[] list = new String[4];
|
||||||
|
int idLength = current_id.length();
|
||||||
|
String subString = current_id.substring(idLength - 4, idLength);
|
||||||
|
for (int i = 0; i < subString.length(); i++) {
|
||||||
|
int num = Character.getNumericValue(subString.charAt(i));
|
||||||
|
int begin = num % 3 + 1;
|
||||||
|
list[i] = e_token.substring(begin, begin + 8);
|
||||||
|
e_token = e_token.substring(begin + 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder v_tks = new StringBuilder();
|
||||||
|
for (String string : list) {
|
||||||
|
v_tks.append(string);
|
||||||
|
}
|
||||||
|
return v_tks.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -10,6 +10,7 @@ import android.os.Handler;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
|
|
||||||
import com.github.catvod.crawler.SpiderDebug;
|
import com.github.catvod.crawler.SpiderDebug;
|
||||||
|
import com.github.catvod.utils.ProxyServer;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
@ -41,7 +42,11 @@ public class Init {
|
||||||
|
|
||||||
public static void init(Context context) {
|
public static void init(Context context) {
|
||||||
get().app = ((Application) context);
|
get().app = ((Application) context);
|
||||||
SpiderDebug.log("自定義爬蟲代碼載入成功!");
|
SpiderDebug.log("自定義爬蟲代碼載入成功!" + "1");
|
||||||
|
execute(() -> {
|
||||||
|
ProxyServer.INSTANCE.stop();
|
||||||
|
ProxyServer.INSTANCE.start();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void execute(Runnable runnable) {
|
public static void execute(Runnable runnable) {
|
||||||
|
|
@ -60,7 +65,8 @@ public class Init {
|
||||||
try {
|
try {
|
||||||
Activity activity = Init.getActivity();
|
Activity activity = Init.getActivity();
|
||||||
if (activity == null || Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return;
|
if (activity == null || Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return;
|
||||||
if (activity.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) return;
|
if (activity.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED)
|
||||||
|
return;
|
||||||
activity.requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 9999);
|
activity.requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 9999);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,98 @@
|
||||||
|
package com.github.catvod.spider;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import com.github.catvod.api.TianYiHandler;
|
||||||
|
import com.github.catvod.api.UCTokenHandler;
|
||||||
|
import com.github.catvod.bean.Class;
|
||||||
|
import com.github.catvod.bean.Result;
|
||||||
|
import com.github.catvod.bean.Vod;
|
||||||
|
import com.github.catvod.crawler.Spider;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class Introduce extends Spider {
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(Context context, String extend) throws Exception {
|
||||||
|
super.init(context, extend);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String homeContent(boolean filter) throws Exception {
|
||||||
|
List<Class> classes = new ArrayList<>();
|
||||||
|
classes.add(new Class("1", "UC"));
|
||||||
|
classes.add(new Class("2", "天意"));
|
||||||
|
List<Vod> list = new ArrayList<>();
|
||||||
|
String pic = "https://androidcatvodspider.netlify.app/wechat.png";
|
||||||
|
String name = "关注公众号";
|
||||||
|
list.add(new Vod("https://androidcatvodspider.netlify.app/wechat.png", name, pic));
|
||||||
|
String pic2 = "https://androidcatvodspider.netlify.app/wechat.png";
|
||||||
|
String name2 = "本接口不收费,请不要付费,谢谢!";
|
||||||
|
list.add(new Vod("https://androidcatvodspider.netlify.app/wechat.png", name2, pic2));
|
||||||
|
String pic3 = "https://androidcatvodspider.netlify.app/wechat.png";
|
||||||
|
String name3 = "2025-04-14 11:00";
|
||||||
|
list.add(new Vod("https://androidcatvodspider.netlify.app/wechat.png", name3, pic3));
|
||||||
|
return Result.string(classes, list);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String categoryContent(String tid, String pg, boolean filter, HashMap<String, String> extend) {
|
||||||
|
List<Vod> vodList = new ArrayList<>();
|
||||||
|
//UC
|
||||||
|
if (tid.equals("1")) {
|
||||||
|
String pic = "https://androidcatvodspider.netlify.app/wechat.png";
|
||||||
|
String name = "点击设置Token";
|
||||||
|
vodList.add(new Vod("UCToken", name, pic));
|
||||||
|
}
|
||||||
|
//天翼
|
||||||
|
if (tid.equals("2")) {
|
||||||
|
String pic = "https://androidcatvodspider.netlify.app/wechat.png";
|
||||||
|
String name = "点击设置cookie";
|
||||||
|
vodList.add(new Vod("天翼cookie", name, pic));
|
||||||
|
String pic1 = "https://androidcatvodspider.netlify.app/wechat.png";
|
||||||
|
String name1 = "清除cookie";
|
||||||
|
vodList.add(new Vod("clean天翼cookie", name1, pic1));
|
||||||
|
}
|
||||||
|
return Result.get().vod(vodList).page().string();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String detailContent(List<String> ids) throws Exception {
|
||||||
|
String vodId = ids.get(0);
|
||||||
|
|
||||||
|
//UC Token 扫码
|
||||||
|
if (vodId.equals("UCToken")) {
|
||||||
|
UCTokenHandler qrCodeHandler = new UCTokenHandler();
|
||||||
|
qrCodeHandler.startUC_TOKENScan();
|
||||||
|
}
|
||||||
|
if (vodId.equals("天翼cookie")) {
|
||||||
|
TianYiHandler qrCodeHandler = TianYiHandler.get();
|
||||||
|
qrCodeHandler.startFlow();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vodId.equals("clean天翼cookie")) {
|
||||||
|
TianYiHandler qrCodeHandler = TianYiHandler.get();
|
||||||
|
qrCodeHandler.cleanCookie();
|
||||||
|
}
|
||||||
|
Vod item = new Vod();
|
||||||
|
item.setVodId(vodId);
|
||||||
|
item.setVodName("公众号");
|
||||||
|
item.setVodPic("https://androidcatvodspider.netlify.app/wechat.png");
|
||||||
|
item.setVodRemarks("");
|
||||||
|
item.setVodPlayFrom("公众号");
|
||||||
|
item.setVodPlayUrl("https://test-streams.mux.dev/x36xhzz/url_6/193039199_mp4_h264_aac_hq_7.m3u8");
|
||||||
|
item.setVodDirector("公众号");
|
||||||
|
return Result.string(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String playerContent(String flag, String id, List<String> vipFlags) throws Exception {
|
||||||
|
return Result.get().url("https://test-streams.mux.dev/x36xhzz/url_6/193039199_mp4_h264_aac_hq_7.m3u8").string();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,104 +0,0 @@
|
||||||
package com.github.catvod.spider;
|
|
||||||
|
|
||||||
import com.github.catvod.bean.Class;
|
|
||||||
import com.github.catvod.bean.Result;
|
|
||||||
import com.github.catvod.bean.Vod;
|
|
||||||
import com.github.catvod.crawler.Spider;
|
|
||||||
import com.github.catvod.net.OkHttp;
|
|
||||||
import com.github.catvod.utils.Util;
|
|
||||||
|
|
||||||
import org.jsoup.Jsoup;
|
|
||||||
import org.jsoup.nodes.Document;
|
|
||||||
import org.jsoup.nodes.Element;
|
|
||||||
|
|
||||||
import java.net.URLEncoder;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Locale;
|
|
||||||
|
|
||||||
public class Jable extends Spider {
|
|
||||||
|
|
||||||
private static final String siteUrl = "https://jable.tv";
|
|
||||||
private static final String cateUrl = siteUrl + "/categories/";
|
|
||||||
private static final String detailUrl = siteUrl + "/videos/";
|
|
||||||
private static final String searchUrl = siteUrl + "/search/";
|
|
||||||
|
|
||||||
private HashMap<String, String> getHeaders() {
|
|
||||||
HashMap<String, String> headers = new HashMap<>();
|
|
||||||
headers.put("User-Agent", Util.CHROME);
|
|
||||||
return headers;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String homeContent(boolean filter) throws Exception {
|
|
||||||
List<Vod> list = new ArrayList<>();
|
|
||||||
List<Class> classes = new ArrayList<>();
|
|
||||||
Document doc = Jsoup.parse(OkHttp.string(cateUrl, getHeaders()));
|
|
||||||
for (Element element : doc.select("div.img-box > a")) {
|
|
||||||
String typeId = element.attr("href").split("/")[4];
|
|
||||||
String typeName = element.select("div.absolute-center > h4").text();
|
|
||||||
classes.add(new Class(typeId, typeName));
|
|
||||||
}
|
|
||||||
doc = Jsoup.parse(OkHttp.string(siteUrl, getHeaders()));
|
|
||||||
for (Element element : doc.select("div.video-img-box")) {
|
|
||||||
String pic = element.select("img").attr("data-src");
|
|
||||||
String url = element.select("a").attr("href");
|
|
||||||
String name = element.select("div.detail > h6").text();
|
|
||||||
if (pic.endsWith(".gif") || name.isEmpty()) continue;
|
|
||||||
String id = url.split("/")[4];
|
|
||||||
list.add(new Vod(id, name, pic));
|
|
||||||
}
|
|
||||||
return Result.string(classes, list);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String categoryContent(String tid, String pg, boolean filter, HashMap<String, String> extend) throws Exception {
|
|
||||||
List<Vod> list = new ArrayList<>();
|
|
||||||
String target = cateUrl + tid + "/?mode=async&function=get_block&block_id=list_videos_common_videos_list&sort_by=post_date&from=" + String.format(Locale.getDefault(), "%02d", Integer.parseInt(pg)) + "&_=" + System.currentTimeMillis();
|
|
||||||
Document doc = Jsoup.parse(OkHttp.string(target, getHeaders()));
|
|
||||||
for (Element element : doc.select("div.video-img-box")) {
|
|
||||||
String pic = element.select("img").attr("data-src");
|
|
||||||
String url = element.select("a").attr("href");
|
|
||||||
String name = element.select("div.detail > h6").text();
|
|
||||||
String id = url.split("/")[4];
|
|
||||||
list.add(new Vod(id, name, pic));
|
|
||||||
}
|
|
||||||
return Result.string(list);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String detailContent(List<String> ids) throws Exception {
|
|
||||||
Document doc = Jsoup.parse(OkHttp.string(detailUrl.concat(ids.get(0)).concat("/"), getHeaders()));
|
|
||||||
String name = doc.select("meta[property=og:title]").attr("content");
|
|
||||||
String pic = doc.select("meta[property=og:image]").attr("content");
|
|
||||||
String year = doc.select("span.inactive-color").get(0).text();
|
|
||||||
Vod vod = new Vod();
|
|
||||||
vod.setVodId(ids.get(0));
|
|
||||||
vod.setVodPic(pic);
|
|
||||||
vod.setVodYear(year.replace("上市於 ", ""));
|
|
||||||
vod.setVodName(name);
|
|
||||||
vod.setVodPlayFrom("Jable");
|
|
||||||
vod.setVodPlayUrl("播放$" + Util.getVar(doc.html(), "hlsUrl"));
|
|
||||||
return Result.string(vod);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String searchContent(String key, boolean quick) throws Exception {
|
|
||||||
List<Vod> list = new ArrayList<>();
|
|
||||||
Document doc = Jsoup.parse(OkHttp.string(searchUrl.concat(URLEncoder.encode(key)).concat("/"), getHeaders()));
|
|
||||||
for (Element element : doc.select("div.video-img-box")) {
|
|
||||||
String pic = element.select("img").attr("data-src");
|
|
||||||
String url = element.select("a").attr("href");
|
|
||||||
String name = element.select("div.detail > h6").text();
|
|
||||||
String id = url.split("/")[4];
|
|
||||||
list.add(new Vod(id, name, pic));
|
|
||||||
}
|
|
||||||
return Result.string(list);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String playerContent(String flag, String id, List<String> vipFlags) throws Exception {
|
|
||||||
return Result.get().url(id).header(getHeaders()).string();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,133 +0,0 @@
|
||||||
package com.github.catvod.spider;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.text.TextUtils;
|
|
||||||
|
|
||||||
import com.github.catvod.bean.Class;
|
|
||||||
import com.github.catvod.bean.Result;
|
|
||||||
import com.github.catvod.bean.Vod;
|
|
||||||
import com.github.catvod.crawler.Spider;
|
|
||||||
import com.github.catvod.net.OkHttp;
|
|
||||||
import com.github.catvod.utils.Util;
|
|
||||||
|
|
||||||
import org.jsoup.Jsoup;
|
|
||||||
import org.jsoup.nodes.Document;
|
|
||||||
import org.jsoup.nodes.Element;
|
|
||||||
import org.jsoup.select.Elements;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Qile
|
|
||||||
*/
|
|
||||||
public class JavDb extends Spider {
|
|
||||||
|
|
||||||
private static String siteUrl = "https://javdb523.com";
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void init(Context context, String extend) throws Exception {
|
|
||||||
if (!extend.isEmpty()) siteUrl = extend;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Map<String, String> getHeader() {
|
|
||||||
Map<String, String> header = new HashMap<>();
|
|
||||||
header.put("User-Agent", Util.CHROME);
|
|
||||||
header.put("Referer", siteUrl + "/");
|
|
||||||
return header;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String homeContent(boolean filter) throws Exception {
|
|
||||||
List<Class> classes = new ArrayList<>();
|
|
||||||
List<String> typeIds = Arrays.asList("", "censored", "uncensored", "western");
|
|
||||||
List<String> typeNames = Arrays.asList("全部", "有码", "无码", "欧美");
|
|
||||||
for (int i = 0; i < typeIds.size(); i++) classes.add(new Class(typeIds.get(i), typeNames.get(i)));
|
|
||||||
Document doc = Jsoup.parse(OkHttp.string(siteUrl, getHeader()));
|
|
||||||
List<Vod> list = new ArrayList<>();
|
|
||||||
for (Element li : doc.select(".item")) {
|
|
||||||
String vid = siteUrl + li.select("a").attr("href");
|
|
||||||
String name = li.select("a").attr("title");
|
|
||||||
String pic = li.select("img").attr("src");
|
|
||||||
list.add(new Vod(vid, name, pic));
|
|
||||||
}
|
|
||||||
return Result.string(classes, list);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String categoryContent(String tid, String pg, boolean filter, HashMap<String, String> extend) throws Exception {
|
|
||||||
String cateUrl = siteUrl + String.format("/%s?page=%s", tid, pg);
|
|
||||||
Document doc = Jsoup.parse(OkHttp.string(cateUrl, getHeader()));
|
|
||||||
List<Vod> list = new ArrayList<>();
|
|
||||||
for (Element li : doc.select(".item")) {
|
|
||||||
String vid = siteUrl + li.select("a").attr("href");
|
|
||||||
String name = li.select("a").attr("title");
|
|
||||||
String pic = li.select("img").attr("src");
|
|
||||||
list.add(new Vod(vid, name, pic));
|
|
||||||
}
|
|
||||||
return Result.string(list);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String detailContent(List<String> ids) throws Exception {
|
|
||||||
Document doc = Jsoup.parse(OkHttp.string(ids.get(0), getHeader()));
|
|
||||||
if (doc.text().contains("歡迎登入")) return Result.error("该资源需要登入");
|
|
||||||
List<String> vodItems = new ArrayList<>();
|
|
||||||
Elements sourceList = doc.select(".item.columns");
|
|
||||||
for (Element a : sourceList) {
|
|
||||||
String episodeUrl = a.select("div a").attr("href");
|
|
||||||
String episodeName = a.select("div a").text();
|
|
||||||
vodItems.add(episodeName + "$" + episodeUrl);
|
|
||||||
}
|
|
||||||
Elements elements = doc.select(".panel-block");
|
|
||||||
String classifyName = "";
|
|
||||||
String year = "";
|
|
||||||
String area = "";
|
|
||||||
String remark = "";
|
|
||||||
for (Element element : elements) {
|
|
||||||
String text = element.text();
|
|
||||||
if (text.startsWith("類別:")) {
|
|
||||||
classifyName = element.select("span a").text();
|
|
||||||
} else if (text.startsWith("片商:")) {
|
|
||||||
area = element.select("span a").text();
|
|
||||||
} else if (text.startsWith("日期:")) {
|
|
||||||
year = element.select("span").text();
|
|
||||||
} else if (text.startsWith("時長:")) {
|
|
||||||
remark = element.select("span").text();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Vod vod = new Vod();
|
|
||||||
vod.setVodId(ids.get(0));
|
|
||||||
vod.setVodYear(year);
|
|
||||||
vod.setVodArea(area);
|
|
||||||
vod.setVodRemarks(remark);
|
|
||||||
vod.setTypeName(classifyName);
|
|
||||||
vod.setVodContent(ids.get(0));
|
|
||||||
vod.setVodPlayFrom("Qile");
|
|
||||||
vod.setVodPlayUrl(TextUtils.join("#", vodItems));
|
|
||||||
return Result.string(vod);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String searchContent(String key, boolean quick) throws Exception {
|
|
||||||
String searchUrl = siteUrl + "/search?q=" + Uri.encode(key) + "&f=all";
|
|
||||||
Document doc = Jsoup.parse(OkHttp.string(searchUrl, getHeader()));
|
|
||||||
List<Vod> list = new ArrayList<>();
|
|
||||||
for (Element li : doc.select(".item")) {
|
|
||||||
String vid = siteUrl + li.select("a").attr("href");
|
|
||||||
String name = li.select("a").attr("title");
|
|
||||||
String pic = li.select("img").attr("src");
|
|
||||||
list.add(new Vod(vid, name, pic));
|
|
||||||
}
|
|
||||||
return Result.string(list);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String playerContent(String flag, String id, List<String> vipFlags) throws Exception {
|
|
||||||
return Result.get().url(id).header(getHeader()).string();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -8,9 +8,13 @@ import com.github.catvod.bean.Vod;
|
||||||
import com.github.catvod.bean.jianpian.Data;
|
import com.github.catvod.bean.jianpian.Data;
|
||||||
import com.github.catvod.bean.jianpian.Detail;
|
import com.github.catvod.bean.jianpian.Detail;
|
||||||
import com.github.catvod.bean.jianpian.Resp;
|
import com.github.catvod.bean.jianpian.Resp;
|
||||||
|
import com.github.catvod.bean.jianpian.Search;
|
||||||
import com.github.catvod.crawler.Spider;
|
import com.github.catvod.crawler.Spider;
|
||||||
|
import com.github.catvod.crawler.SpiderDebug;
|
||||||
import com.github.catvod.net.OkHttp;
|
import com.github.catvod.net.OkHttp;
|
||||||
import com.github.catvod.utils.Json;
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonParser;
|
||||||
|
|
||||||
import java.net.URLEncoder;
|
import java.net.URLEncoder;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
@ -24,66 +28,87 @@ import java.util.Map;
|
||||||
*/
|
*/
|
||||||
public class Jianpian extends Spider {
|
public class Jianpian extends Spider {
|
||||||
|
|
||||||
private final String siteUrl = "http://api2.rinhome.com";
|
private String siteUrl = "https://ev5356.970xw.com";
|
||||||
|
private String imgDomain;
|
||||||
private String extend;
|
private String extend;
|
||||||
|
|
||||||
private Map<String, String> getHeader() {
|
private Map<String, String> getHeader() {
|
||||||
Map<String, String> headers = new HashMap<>();
|
Map<String, String> headers = new HashMap<>();
|
||||||
headers.put("User-Agent", "jianpian-android/360");
|
headers.put("User-Agent", "Mozilla/5.0 (Linux; Android 9; V2196A Build/PQ3A.190705.08211809; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/91.0.4472.114 Mobile Safari/537.36;webank/h5face;webank/1.0;netType:NETWORK_WIFI;appVersion:416;packageName:com.jp3.xg3");
|
||||||
headers.put("JPAUTH", "y261ow7kF2dtzlxh1GS9EB8nbTxNmaK/QQIAjctlKiEv");
|
headers.put("Referer", siteUrl);
|
||||||
return headers;
|
return headers;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init(Context context, String extend) throws Exception {
|
public void init(Context context, String extend) throws Exception {
|
||||||
this.extend = extend;
|
this.extend = extend;
|
||||||
|
JsonObject domains = new Gson().fromJson(OkHttp.string("https://dns.alidns.com/resolve?name=swrdsfeiujo25sw.cc&type=TXT"), JsonObject.class);
|
||||||
|
String parts = domains.getAsJsonArray("Answer").get(0).getAsJsonObject().get("data").getAsString();
|
||||||
|
parts = parts.replace("\"", "");
|
||||||
|
String[] domain = parts.split(",");
|
||||||
|
for (String d : domain) {
|
||||||
|
siteUrl = "https://wangerniu." + d;
|
||||||
|
String json = OkHttp.string(siteUrl + "/api/appAuthConfig");
|
||||||
|
if (!json.isEmpty()) {
|
||||||
|
JsonObject root = new Gson().fromJson(json, JsonObject.class);
|
||||||
|
imgDomain = root.getAsJsonObject("data").get("imgDomain").getAsString();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String homeContent(boolean filter) throws Exception {
|
public String homeContent(boolean filter) throws Exception {
|
||||||
List<Class> classes = new ArrayList<>();
|
List<Class> classes = new ArrayList<>();
|
||||||
List<String> typeIds = Arrays.asList("0", "1", "2", "3", "4");
|
List<String> typeIds = Arrays.asList("1", "2", "3", "4", "50", "99");
|
||||||
List<String> typeNames = Arrays.asList("全部", "电影", "电视剧", "动漫", "综艺");
|
List<String> typeNames = Arrays.asList("電影", "電視劇", "動漫", "綜藝", "紀錄片", "Netflix");
|
||||||
for (int i = 0; i < typeIds.size(); i++) classes.add(new Class(typeIds.get(i), typeNames.get(i)));
|
for (int i = 0; i < typeIds.size(); i++) classes.add(new Class(typeIds.get(i), typeNames.get(i)));
|
||||||
return Result.string(classes, Json.parse(OkHttp.string(extend)));
|
return Result.string(classes, JsonParser.parseString(OkHttp.string(extend)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String homeVideoContent() {
|
public String homeVideoContent() {
|
||||||
List<Vod> list = new ArrayList<>();
|
List<Vod> list = new ArrayList<>();
|
||||||
String url = siteUrl + "/api/slide/list?code=unknown9039b6856c3a3306&pos_id=888&channel=wandoujia";
|
String url = siteUrl + "/api/slide/list?pos_id=88";
|
||||||
Resp resp = Resp.objectFrom(OkHttp.string(url, getHeader()));
|
Resp resp = Resp.objectFrom(OkHttp.string(url, getHeader()));
|
||||||
for (Data data : resp.getData()) list.add(data.vod());
|
for (Data data : resp.getData()) list.add(data.homeVod(imgDomain));
|
||||||
return Result.string(list);
|
return Result.string(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String categoryContent(String tid, String pg, boolean filter, HashMap<String, String> extend) throws Exception {
|
public String categoryContent(String tid, String pg, boolean filter, HashMap<String, String> extend) throws Exception {
|
||||||
if (tid.endsWith("/{pg}")) return searchContent(tid.split("/")[0], pg);
|
if (tid.endsWith("/{pg}")) return searchContent(tid.split("/")[0], pg);
|
||||||
List<Vod> list = new ArrayList<>();
|
if (tid.equals("50") || tid.equals("99") || tid.equals("111")) {
|
||||||
HashMap<String, String> ext = new HashMap<>();
|
List<Vod> list = new ArrayList<>();
|
||||||
if (extend != null && extend.size() > 0) ext.putAll(extend);
|
String url = siteUrl + String.format("/api/dyTag/list?category_id=%s&page=%s", tid, pg);
|
||||||
String cateId = ext.get("cateId") == null ? tid : ext.get("cateId");
|
Resp resp = Resp.objectFrom(OkHttp.string(url, getHeader()));
|
||||||
String area = ext.get("area") == null ? "0" : ext.get("area");
|
for (Data data : resp.getData()) for (Data dataList : data.getDataList()) list.add(dataList.vod(imgDomain));
|
||||||
String year = ext.get("year") == null ? "0" : ext.get("year");
|
return Result.get().page().vod(list).string();
|
||||||
String by = ext.get("by") == null ? "hot" : ext.get("by");
|
} else {
|
||||||
String url = siteUrl + String.format("/api/crumb/list?area=%s&category_id=%s&page=%s&type=0&limit=24&sort=%s&year=%s", area, cateId, pg, by, year);
|
List<Vod> list = new ArrayList<>();
|
||||||
Resp resp = Resp.objectFrom(OkHttp.string(url, getHeader()));
|
HashMap<String, String> ext = new HashMap<>();
|
||||||
for (Data data : resp.getData()) list.add(data.vod());
|
if (extend != null && !extend.isEmpty()) ext.putAll(extend);
|
||||||
return Result.string(list);
|
String area = ext.get("area") == null ? "0" : ext.get("area");
|
||||||
|
String year = ext.get("year") == null ? "0" : ext.get("year");
|
||||||
|
String by = ext.get("by") == null ? "updata" : ext.get("by");
|
||||||
|
String url = siteUrl + String.format("/api/crumb/list?fcate_pid=%s&area=%s&year=%s&type=0&sort=%s&page=%s&category_id=", tid, area, year, by, pg);
|
||||||
|
Resp resp = Resp.objectFrom(OkHttp.string(url, getHeader()));
|
||||||
|
for (Data data : resp.getData()) list.add(data.vod(imgDomain));
|
||||||
|
return Result.string(list);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String detailContent(List<String> ids) throws Exception {
|
public String detailContent(List<String> ids) throws Exception {
|
||||||
String url = siteUrl + "/api/node/detail?channel=wandoujia&token=&id=" + ids.get(0);
|
String url = siteUrl + "/api/video/detailv2?id=" + ids.get(0);
|
||||||
Data data = Detail.objectFrom(OkHttp.string(url, getHeader())).getData();
|
Data data = Detail.objectFrom(OkHttp.string(url, getHeader())).getData();
|
||||||
Vod vod = data.vod();
|
Vod vod = data.vod(imgDomain);
|
||||||
vod.setVodPlayFrom("Jianpian");
|
vod.setVodPlayFrom(data.getVodFrom());
|
||||||
vod.setVodYear(data.getYear());
|
vod.setVodYear(data.getYear());
|
||||||
vod.setVodArea(data.getArea());
|
vod.setVodArea(data.getArea());
|
||||||
vod.setTypeName(data.getTypes());
|
vod.setTypeName(data.getTypes());
|
||||||
vod.setVodActor(data.getActors());
|
vod.setVodActor(data.getActors());
|
||||||
vod.setVodPlayUrl(data.getPlayUrl());
|
vod.setVodPlayUrl(data.getVodUrl());
|
||||||
vod.setVodDirector(data.getDirectors());
|
vod.setVodDirector(data.getDirectors());
|
||||||
vod.setVodContent(data.getDescription());
|
vod.setVodContent(data.getDescription());
|
||||||
return Result.string(vod);
|
return Result.string(vod);
|
||||||
|
|
@ -106,9 +131,9 @@ public class Jianpian extends Spider {
|
||||||
|
|
||||||
public String searchContent(String key, String pg) throws Exception {
|
public String searchContent(String key, String pg) throws Exception {
|
||||||
List<Vod> list = new ArrayList<>();
|
List<Vod> list = new ArrayList<>();
|
||||||
String url = siteUrl + "/api/video/search?page=" + pg + "&key=" + URLEncoder.encode(key);
|
String url = siteUrl + String.format("/api/v2/search/videoV2?key=%s&category_id=88&page=%s&pageSize=20", URLEncoder.encode(key), pg);
|
||||||
Resp resp = Resp.objectFrom(OkHttp.string(url, getHeader()));
|
Search search = Search.objectFrom(OkHttp.string(url, getHeader()));
|
||||||
for (Data data : resp.getData()) list.add(data.vod());
|
for (Search data : search.getData()) list.add(data.vod(imgDomain));
|
||||||
return Result.string(list);
|
return Result.string(list);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -145,7 +145,7 @@ public class JustLive extends Spider {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String searchContent(String key, boolean quick) throws Exception {
|
public String searchContent(String key, boolean quick) throws Exception {
|
||||||
String searchUrl = siteUrl + "/api/live/search?platform=all&keyWords=" + URLEncoder.encode(key) + "&uid=35717d71548f4ec9ab6f327cc16ad2bf";
|
String searchUrl = siteUrl + "/api/live/search?platform=all&keyWords=" + URLEncoder.encode(key) + "&uid=f12b7678af364cb4b3c318559ade200e";
|
||||||
String content = OkHttp.string(searchUrl, getHeader());
|
String content = OkHttp.string(searchUrl, getHeader());
|
||||||
List<Vod> list = new ArrayList<>();
|
List<Vod> list = new ArrayList<>();
|
||||||
JSONArray dataArray = new JSONObject(content).getJSONArray("data");
|
JSONArray dataArray = new JSONObject(content).getJSONArray("data");
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,152 @@
|
||||||
|
package com.github.catvod.spider;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import com.github.catvod.bean.Class;
|
||||||
|
import com.github.catvod.bean.Result;
|
||||||
|
import com.github.catvod.bean.Vod;
|
||||||
|
import com.github.catvod.net.OkHttp;
|
||||||
|
import com.github.catvod.utils.Util;
|
||||||
|
|
||||||
|
import org.jsoup.Jsoup;
|
||||||
|
import org.jsoup.nodes.Document;
|
||||||
|
import org.jsoup.nodes.Element;
|
||||||
|
import org.jsoup.select.Elements;
|
||||||
|
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 夸克吧
|
||||||
|
*
|
||||||
|
* @author lushunming
|
||||||
|
*/
|
||||||
|
public class KuaKeBa extends Cloud {
|
||||||
|
|
||||||
|
private final String siteUrl = "https://www.kuakeba.top/yunpan";
|
||||||
|
private final String hostUrl = "https://www.kuakeba.top";
|
||||||
|
|
||||||
|
|
||||||
|
private Map<String, String> getHeader() {
|
||||||
|
Map<String, String> header = new HashMap<>();
|
||||||
|
header.put("User-Agent", Util.CHROME);
|
||||||
|
return header;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, String> getHeaderWithCookie() {
|
||||||
|
Map<String, String> header = new HashMap<>();
|
||||||
|
header.put("User-Agent", Util.CHROME);
|
||||||
|
header.put("cookie", "esc_search_captcha=1; result=43");
|
||||||
|
return header;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(Context context, String extend) throws Exception {
|
||||||
|
|
||||||
|
super.init(context, extend);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String homeContent(boolean filter) {
|
||||||
|
List<Class> classes = new ArrayList<>();
|
||||||
|
Document doc = Jsoup.parse(OkHttp.string(siteUrl, getHeader()));
|
||||||
|
Elements elements = doc.select(" div.catleader > ul > li > a");
|
||||||
|
for (Element e : elements) {
|
||||||
|
String url = e.attr("href");
|
||||||
|
String name = e.text();
|
||||||
|
classes.add(new Class(url, name));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.string(classes, parseVodListFromDoc(doc));
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Vod> parseVodListFromDoc(Document doc) {
|
||||||
|
List<Vod> list = new ArrayList<>();
|
||||||
|
Elements elements = doc.select(" article.excerpt");
|
||||||
|
for (Element e : elements) {
|
||||||
|
String vodId = e.selectFirst(" a").attr("href");
|
||||||
|
String vodPic = e.selectFirst(" img").attr("data-src");
|
||||||
|
if (!vodPic.startsWith("http")) {
|
||||||
|
vodPic = hostUrl + vodPic;
|
||||||
|
}
|
||||||
|
String vodName = e.selectFirst(" header > h2 > a").text();
|
||||||
|
String vodRemarks = "";
|
||||||
|
list.add(new Vod(vodId, vodName, vodPic, vodRemarks));
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String categoryContent(String tid, String pg, boolean filter, HashMap<String, String> extend) {
|
||||||
|
|
||||||
|
Document doc = Jsoup.parse(OkHttp.string(String.format("%s/page/%s", tid, pg), getHeader()));
|
||||||
|
List<Vod> list = parseVodListFromDoc(doc);
|
||||||
|
int total = (Integer.parseInt(pg) + 1) * 20;
|
||||||
|
return Result.get().vod(list).page(Integer.parseInt(pg), Integer.parseInt(pg) + 1, 20, total).string();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String detailContent(List<String> ids) throws Exception {
|
||||||
|
String vodId = ids.get(0);
|
||||||
|
Document doc = Jsoup.parse(OkHttp.string(vodId, getHeader()));
|
||||||
|
|
||||||
|
Vod item = new Vod();
|
||||||
|
item.setVodId(vodId);
|
||||||
|
item.setVodName(doc.selectFirst(" h1.article-title > a").text());
|
||||||
|
item.setVodPic(doc.selectFirst(" img").attr("src"));
|
||||||
|
item.setVodArea(getStrByRegex(Pattern.compile("导演:(.*?)<br"), doc.html()));
|
||||||
|
item.setVodDirector(getStrByRegex(Pattern.compile("地区:(.*?)<br"), doc.html()));
|
||||||
|
item.setVodActor(getStrByRegex(Pattern.compile("主演:(.*?)<br"), doc.html()));
|
||||||
|
item.setVodYear(getStrByRegex(Pattern.compile("上映日期:(.*?)<br"), doc.html()));
|
||||||
|
item.setVodRemarks("");
|
||||||
|
item.setVodContent(getStrByRegex(Pattern.compile("<h2>剧情简介</h2>\n(.*?)</p>"), doc.html()).replace("<p>", ""));
|
||||||
|
|
||||||
|
List<String> shareLinks = new ArrayList<>();
|
||||||
|
List<String> tags = new ArrayList<>();
|
||||||
|
for (Element element : doc.select("article.article-content p a")) {
|
||||||
|
if (element.attr("href").matches(Util.patternQuark)) {
|
||||||
|
shareLinks.add(element.attr("href").trim());
|
||||||
|
} else if (element.attr("href").contains("tag")) {
|
||||||
|
tags.add(element.attr("href"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
item.setTypeName(String.join(",", tags));
|
||||||
|
|
||||||
|
item.setVodPlayUrl(super.detailContentVodPlayUrl(shareLinks));
|
||||||
|
item.setVodPlayFrom(super.detailContentVodPlayFrom(shareLinks));
|
||||||
|
|
||||||
|
return Result.string(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getStrByRegex(Pattern pattern, String str) {
|
||||||
|
Matcher matcher = pattern.matcher(str);
|
||||||
|
if (matcher.find()) return matcher.group(1).trim();
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String searchContent(String key, boolean quick) throws Exception {
|
||||||
|
return searchContent(key, "1");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String searchContent(String key, boolean quick, String pg) throws Exception {
|
||||||
|
return searchContent(key, pg);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String searchContent(String key, String pg) {
|
||||||
|
String searchURL = siteUrl + String.format("/?s=%s", URLEncoder.encode(key));
|
||||||
|
String html = OkHttp.string(searchURL, getHeaderWithCookie());
|
||||||
|
Document doc = Jsoup.parse(html);
|
||||||
|
|
||||||
|
return Result.string(parseVodListFromDoc(doc));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,153 @@
|
||||||
|
package com.github.catvod.spider;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import com.github.catvod.bean.Class;
|
||||||
|
import com.github.catvod.bean.Result;
|
||||||
|
import com.github.catvod.bean.Vod;
|
||||||
|
import com.github.catvod.net.OkHttp;
|
||||||
|
import com.github.catvod.utils.Json;
|
||||||
|
import com.github.catvod.utils.Util;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import org.jsoup.Jsoup;
|
||||||
|
import org.jsoup.nodes.Document;
|
||||||
|
import org.jsoup.nodes.Element;
|
||||||
|
import org.jsoup.select.Elements;
|
||||||
|
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 夸克社
|
||||||
|
*
|
||||||
|
* @author lushunming
|
||||||
|
* @createdate 2024-12-03
|
||||||
|
*/
|
||||||
|
public class KuaKeS extends Cloud {
|
||||||
|
|
||||||
|
private final String siteUrl = "https://kuakes.com";
|
||||||
|
|
||||||
|
|
||||||
|
private Map<String, String> getHeader() {
|
||||||
|
Map<String, String> header = new HashMap<>();
|
||||||
|
header.put("User-Agent", Util.CHROME);
|
||||||
|
return header;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, String> getHeaderWithCookie() {
|
||||||
|
Map<String, String> header = new HashMap<>();
|
||||||
|
header.put("User-Agent", Util.CHROME);
|
||||||
|
header.put("cookie", "esc_search_captcha=1; result=43");
|
||||||
|
return header;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(Context context, String extend) throws Exception {
|
||||||
|
|
||||||
|
super.init(context, extend);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String homeContent(boolean filter) {
|
||||||
|
List<Class> classes = new ArrayList<>();
|
||||||
|
Document doc = Jsoup.parse(OkHttp.string(siteUrl, getHeader()));
|
||||||
|
Elements elements = doc.select(" #J_topNavMb a");
|
||||||
|
for (Element e : elements) {
|
||||||
|
String url = e.attr("href");
|
||||||
|
String name = e.text();
|
||||||
|
if (url.contains(siteUrl)) {
|
||||||
|
classes.add(new Class(url, name));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.string(classes, parseVodListFromDoc(doc));
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Vod> parseVodListFromDoc(Document doc) {
|
||||||
|
List<Vod> list = new ArrayList<>();
|
||||||
|
Elements elements = doc.select(" div.articles-list > article.post");
|
||||||
|
for (Element e : elements) {
|
||||||
|
String vodId = e.selectFirst(" a.post-title").attr("href");
|
||||||
|
String vodPic = e.selectFirst(" img").attr("data-lazy-src");
|
||||||
|
if (!vodPic.startsWith("http")) {
|
||||||
|
vodPic = siteUrl + vodPic;
|
||||||
|
}
|
||||||
|
String vodName = e.selectFirst("a.post-title").text();
|
||||||
|
String vodRemarks = Objects.nonNull(e.selectFirst("span.db_score")) ? e.selectFirst("span.db_score").text() : "";
|
||||||
|
;
|
||||||
|
list.add(new Vod(vodId, vodName, vodPic, vodRemarks));
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String categoryContent(String tid, String pg, boolean filter, HashMap<String, String> extend) {
|
||||||
|
|
||||||
|
Document doc = Jsoup.parse(OkHttp.string(String.format("%s/page/%s", tid, pg), getHeader()));
|
||||||
|
List<Vod> list = parseVodListFromDoc(doc);
|
||||||
|
int total = (Integer.parseInt(pg) + 1) * 19;
|
||||||
|
return Result.get().vod(list).page(Integer.parseInt(pg), Integer.parseInt(pg) + 1, 19, total).string();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String detailContent(List<String> ids) throws Exception {
|
||||||
|
String vodId = ids.get(0);
|
||||||
|
Document doc = Jsoup.parse(OkHttp.string(vodId, getHeader()));
|
||||||
|
|
||||||
|
Vod item = new Vod();
|
||||||
|
item.setVodId(vodId);
|
||||||
|
item.setVodName(doc.selectFirst(" h1.title-detail").text());
|
||||||
|
item.setVodPic(doc.selectFirst("div.media-pic img").attr("data-lazy-src"));
|
||||||
|
String html = doc.select("div.wp-block-media-text__content > p").text();
|
||||||
|
item.setVodDirector(getStrByRegex(Pattern.compile("导演:(.*?)编剧:"), html));
|
||||||
|
item.setVodArea(getStrByRegex(Pattern.compile("地区:(.*?)语言:"), html));
|
||||||
|
item.setVodActor(getStrByRegex(Pattern.compile("主演:(.*?)类型:"), html));
|
||||||
|
item.setVodYear(getStrByRegex(Pattern.compile("上映日期:(.*?)\\()"), html));
|
||||||
|
item.setTypeName(getStrByRegex(Pattern.compile("类型:(.*?)制片"), html));
|
||||||
|
item.setVodRemarks("");
|
||||||
|
item.setVodContent(doc.select("div.article-detail > p").text());
|
||||||
|
|
||||||
|
List<String> shareLinks = new ArrayList<>();
|
||||||
|
Elements elements = doc.select("div.magicpost-cont-bd > a");
|
||||||
|
String rid = elements.attr("data-rid");
|
||||||
|
String pid = elements.attr("data-pid");
|
||||||
|
String action = "wb_mpdl_front";
|
||||||
|
|
||||||
|
String result = OkHttp.post("https://kuakes.com/wp-admin/admin-ajax.php", Map.of("action", action, "rid", rid, "pid", pid));
|
||||||
|
JsonObject object = Json.safeObject(result);
|
||||||
|
JsonObject data = object.get("data").getAsJsonObject();
|
||||||
|
String url = data.get("url").getAsString();
|
||||||
|
shareLinks.add(url);
|
||||||
|
item.setVodPlayUrl(super.detailContentVodPlayUrl(shareLinks));
|
||||||
|
item.setVodPlayFrom(super.detailContentVodPlayFrom(shareLinks));
|
||||||
|
|
||||||
|
return Result.string(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getStrByRegex(Pattern pattern, String str) {
|
||||||
|
Matcher matcher = pattern.matcher(str);
|
||||||
|
if (matcher.find()) return matcher.group(1).trim();
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String searchContent(String key, boolean quick) throws Exception {
|
||||||
|
return searchContent(key, "1");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String searchContent(String key, boolean quick, String pg) throws Exception {
|
||||||
|
return searchContent(key, pg);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String searchContent(String key, String pg) {
|
||||||
|
String searchURL = siteUrl + String.format("/?s=%s", URLEncoder.encode(key));
|
||||||
|
String html = OkHttp.string(searchURL, getHeaderWithCookie());
|
||||||
|
Document doc = Jsoup.parse(html);
|
||||||
|
|
||||||
|
return Result.string(parseVodListFromDoc(doc));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,164 @@
|
||||||
|
package com.github.catvod.spider;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import com.github.catvod.bean.Class;
|
||||||
|
import com.github.catvod.bean.Result;
|
||||||
|
import com.github.catvod.bean.Vod;
|
||||||
|
import com.github.catvod.net.OkHttp;
|
||||||
|
import com.github.catvod.utils.Json;
|
||||||
|
import com.github.catvod.utils.Util;
|
||||||
|
import com.google.gson.JsonArray;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.jsoup.Jsoup;
|
||||||
|
import org.jsoup.nodes.Document;
|
||||||
|
import org.jsoup.nodes.Element;
|
||||||
|
import org.jsoup.select.Elements;
|
||||||
|
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
public class LeiJing extends Cloud {
|
||||||
|
private static final String siteUrl = "https://leijing1.com/";
|
||||||
|
|
||||||
|
private final String hostUrl = siteUrl;
|
||||||
|
|
||||||
|
|
||||||
|
private Map<String, String> getHeader() {
|
||||||
|
Map<String, String> header = new HashMap<>();
|
||||||
|
header.put("User-Agent", Util.CHROME);
|
||||||
|
return header;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, String> getHeaderWithCookie() {
|
||||||
|
Map<String, String> header = new HashMap<>();
|
||||||
|
header.put("User-Agent", Util.CHROME);
|
||||||
|
header.put("cookie", "esc_search_captcha=1; result=43");
|
||||||
|
return header;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(Context context, String extend) throws Exception {
|
||||||
|
|
||||||
|
super.init(context, extend);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String homeContent(boolean filter) {
|
||||||
|
List<Class> classes = new ArrayList<>();
|
||||||
|
Document doc = Jsoup.parse(OkHttp.string(siteUrl, getHeader()));
|
||||||
|
Elements elements = doc.select(" #tabNavigation > a.tab");
|
||||||
|
for (Element e : elements) {
|
||||||
|
String url = e.attr("href");
|
||||||
|
String name = e.text();
|
||||||
|
if (StringUtils.isNoneBlank(url)) {
|
||||||
|
classes.add(new Class(url, name));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.string(classes, parseVodListFromDoc(doc));
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Vod> parseVodListFromDoc(Document doc) {
|
||||||
|
List<Vod> list = new ArrayList<>();
|
||||||
|
|
||||||
|
Elements topicItems = doc.select(".topicItem");
|
||||||
|
|
||||||
|
for (Element each : topicItems) {
|
||||||
|
// 检查是否有锁定标记
|
||||||
|
if (each.select(".cms-lock-solid").size() > 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提取href
|
||||||
|
String href = each.select("h2 a").attr("href");
|
||||||
|
// 提取标题并处理空格
|
||||||
|
String title = each.select("h2 a").text().trim().replaceAll("\\s+", " ");
|
||||||
|
// 提取摘要
|
||||||
|
String r = each.select(".summary").text();
|
||||||
|
// 提取标签
|
||||||
|
String tag = each.select(".tag").text();
|
||||||
|
|
||||||
|
// 过滤条件
|
||||||
|
if (r.contains("content") && !r.contains("cloud")) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (tag.contains("软件") || tag.contains("游戏") || tag.contains("书籍") || tag.contains("图片") || tag.contains("公告") || tag.contains("音乐") || tag.contains("课程")) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建Video对象
|
||||||
|
Vod video = new Vod(href, title, "", "");
|
||||||
|
list.add(video);
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String categoryContent(String tid, String pg, boolean filter, HashMap<String, String> extend) {
|
||||||
|
|
||||||
|
Document doc = Jsoup.parse(OkHttp.string(String.format("%s%s&page=%s", siteUrl, tid, pg), getHeader()));
|
||||||
|
List<Vod> list = parseVodListFromDoc(doc);
|
||||||
|
int total = (Integer.parseInt(pg) + 1) * 30;
|
||||||
|
return Result.get().vod(list).page(Integer.parseInt(pg), Integer.parseInt(pg) + 1, 30, total).string();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String detailContent(List<String> ids) throws Exception {
|
||||||
|
String vodId = ids.get(0);
|
||||||
|
Document doc = Jsoup.parse(OkHttp.string(siteUrl + vodId, getHeader()));
|
||||||
|
|
||||||
|
|
||||||
|
Vod item = new Vod();
|
||||||
|
item.setVodId(vodId);
|
||||||
|
item.setVodName(doc.selectFirst("div.title").text());
|
||||||
|
|
||||||
|
Elements elements = doc.select("a");
|
||||||
|
List<String> shareLinks = new ArrayList<>();
|
||||||
|
|
||||||
|
for (Element element : elements) {
|
||||||
|
if (element.attr("href").contains("https://cloud.189.cn")) {
|
||||||
|
shareLinks.add(element.attr("href"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
item.setVodPlayUrl(super.detailContentVodPlayUrl(shareLinks));
|
||||||
|
item.setVodPlayFrom(super.detailContentVodPlayFrom(shareLinks));
|
||||||
|
|
||||||
|
return Result.string(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getStrByRegex(Pattern pattern, String str) {
|
||||||
|
Matcher matcher = pattern.matcher(str);
|
||||||
|
if (matcher.find()) return matcher.group(1).trim();
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String searchContent(String key, boolean quick) throws Exception {
|
||||||
|
return searchContent(key, "1");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String searchContent(String key, boolean quick, String pg) throws Exception {
|
||||||
|
return searchContent(key, pg);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String searchContent(String key, String pg) {
|
||||||
|
String searchURL = siteUrl + String.format("search?keyword=%s", URLEncoder.encode(key));
|
||||||
|
String html = OkHttp.string(searchURL, getHeaderWithCookie());
|
||||||
|
Document doc = Jsoup.parse(html);
|
||||||
|
|
||||||
|
return Result.string(parseVodListFromDoc(doc));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,247 @@
|
||||||
|
package com.github.catvod.spider;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import com.github.catvod.bean.Class;
|
||||||
|
import com.github.catvod.bean.Result;
|
||||||
|
import com.github.catvod.bean.Vod;
|
||||||
|
import com.github.catvod.crawler.SpiderDebug;
|
||||||
|
import com.github.catvod.net.OkHttp;
|
||||||
|
import com.github.catvod.utils.Json;
|
||||||
|
import com.github.catvod.utils.Util;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
import org.jsoup.Jsoup;
|
||||||
|
import org.jsoup.nodes.Document;
|
||||||
|
import org.jsoup.nodes.Element;
|
||||||
|
import org.jsoup.select.Elements;
|
||||||
|
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
public class Libvio extends Cloud {
|
||||||
|
|
||||||
|
private static String siteUrl = "https://www.libvio.link/";
|
||||||
|
|
||||||
|
|
||||||
|
private static final String MOBILE_UA = "Mozilla/5.0 (Linux; Android 11; M2007J3SC Build/RKQ1.200826.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/77.0.3865.120 MQQBrowser/6.2 TBS/045714 Mobile Safari/537.36";
|
||||||
|
|
||||||
|
private HashMap<String, String> getHeaders() {
|
||||||
|
HashMap<String, String> headers = new HashMap<>();
|
||||||
|
headers.put("User-Agent", MOBILE_UA);
|
||||||
|
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
private HashMap<String, String> getHeaders(String refer) {
|
||||||
|
HashMap<String, String> headers = new HashMap<>();
|
||||||
|
headers.put("User-Agent", MOBILE_UA);
|
||||||
|
headers.put("Referer", refer);
|
||||||
|
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(Context context, String extend) throws Exception {
|
||||||
|
JsonElement json = Json.parse(extend);
|
||||||
|
super.init(context, extend);
|
||||||
|
String html = OkHttp.string(json.getAsJsonObject().get("site").getAsString());
|
||||||
|
Document doc = Jsoup.parse(html);
|
||||||
|
for (Element element : doc.select(" a")) {
|
||||||
|
if (element.text().contains("可用")) {
|
||||||
|
siteUrl = element.attr("href");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SpiderDebug.log("libvio跳转地址 =====>" + siteUrl); // js_debug.log
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String homeContent(boolean filter) throws Exception {
|
||||||
|
List<Vod> list = new ArrayList<>();
|
||||||
|
List<Class> classes = new ArrayList<>();
|
||||||
|
|
||||||
|
|
||||||
|
String link = siteUrl;
|
||||||
|
Document doc = Jsoup.parse(OkHttp.string(link, getHeaders()));
|
||||||
|
for (Element element : doc.select("ul.stui-header__menu > li > a")) {
|
||||||
|
classes.add(new Class(element.attr("href").replace(".html", ""), element.text()));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Element element : doc.select("ul.stui-vodlist > li > div > a")) {
|
||||||
|
|
||||||
|
String pic = element.attr("data-original");
|
||||||
|
String url = element.attr("href");
|
||||||
|
String name = element.attr("title");
|
||||||
|
if (!pic.startsWith("http")) {
|
||||||
|
pic = siteUrl + pic;
|
||||||
|
}
|
||||||
|
String id = url.split("/")[2];
|
||||||
|
list.add(new Vod(id, name, pic));
|
||||||
|
|
||||||
|
}
|
||||||
|
return Result.string(classes, list);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String MD5(String string) {
|
||||||
|
// 创建 MD5 实例
|
||||||
|
try {
|
||||||
|
MessageDigest md = MessageDigest.getInstance("MD5");
|
||||||
|
// 计算 MD5 哈希值
|
||||||
|
byte[] hashBytes = md.digest(string.getBytes());
|
||||||
|
|
||||||
|
// 将字节数组转换为十六进制字符串表示
|
||||||
|
StringBuilder hexString = new StringBuilder();
|
||||||
|
for (byte hashByte : hashBytes) {
|
||||||
|
String hex = Integer.toHexString(0xff & hashByte);
|
||||||
|
if (hex.length() == 1) {
|
||||||
|
hexString.append('0');
|
||||||
|
}
|
||||||
|
hexString.append(hex);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 输出加密后的 MD5 字符串
|
||||||
|
System.out.println("MD5 加密: " + hexString.toString());
|
||||||
|
return hexString.toString();
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String categoryContent(String tid, String pg, boolean filter, HashMap<String, String> extend) throws Exception {
|
||||||
|
List<Vod> list = new ArrayList<>();
|
||||||
|
|
||||||
|
String target = siteUrl + tid + "-" + pg + ".html";
|
||||||
|
Document doc = Jsoup.parse(OkHttp.string(target, getHeaders()));
|
||||||
|
for (Element element : doc.select("ul.stui-vodlist > li > div > a")) {
|
||||||
|
|
||||||
|
String pic = element.attr("data-original");
|
||||||
|
String url = element.attr("href");
|
||||||
|
String name = element.attr("title");
|
||||||
|
if (!pic.startsWith("http")) {
|
||||||
|
pic = siteUrl + pic;
|
||||||
|
}
|
||||||
|
String id = url.split("/")[2];
|
||||||
|
list.add(new Vod(id, name, pic));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Integer total = (Integer.parseInt(pg) + 1) * 20;
|
||||||
|
return Result.string(Integer.parseInt(pg), Integer.parseInt(pg) + 1, 20, total, list);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String detailContent(List<String> ids) throws Exception {
|
||||||
|
Document doc = Jsoup.parse(OkHttp.string(siteUrl.concat("/detail/").concat(ids.get(0)), getHeaders()));
|
||||||
|
String name = doc.select("body > div:nth-child(3) > div.row > div > div > div > div.stui-content > div.stui-content__detail > h1").text();
|
||||||
|
String pic = doc.select("div.stui-content__thumb > a >img").attr("data-original");
|
||||||
|
// 播放源
|
||||||
|
Elements tabs = doc.select("div.stui-vodlist__head > div > h3");
|
||||||
|
Elements list = doc.select("div.stui-vodlist__head > ul.stui-content__playlist ");
|
||||||
|
|
||||||
|
Vod.VodPlayBuilder builder = new Vod.VodPlayBuilder();
|
||||||
|
List<String> quarkList = new ArrayList<>();
|
||||||
|
for (int i = 0; i < tabs.size(); i++) {
|
||||||
|
List<Vod.VodPlayBuilder.PlayUrl> playUrls = new ArrayList<>();
|
||||||
|
|
||||||
|
String tabName = tabs.get(i).text();
|
||||||
|
|
||||||
|
Elements li = list.get(i).select("a");
|
||||||
|
for (Element element : li) {
|
||||||
|
if (tabName.contains("夸克") || tabName.contains("UC")) {
|
||||||
|
quarkList.add(element.attr("href"));
|
||||||
|
} else {
|
||||||
|
Vod.VodPlayBuilder.PlayUrl playUrl = new Vod.VodPlayBuilder.PlayUrl();
|
||||||
|
playUrl.name = element.text();
|
||||||
|
playUrl.url = element.attr("href").replace("/play/", "");
|
||||||
|
playUrls.add(playUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if (!tabName.contains("夸克") && !tabName.contains("UC")) {
|
||||||
|
builder.append(tabName, playUrls);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
List<String> shareLinks = new ArrayList<>();
|
||||||
|
String quarkNames = "";
|
||||||
|
String quarkUrls = "";
|
||||||
|
|
||||||
|
if (!quarkList.isEmpty()) {
|
||||||
|
for (String s : quarkList) {
|
||||||
|
Document detailPageDoc = Jsoup.parse(OkHttp.string(siteUrl.concat(s), getHeaders()));
|
||||||
|
Matcher matcher = Pattern.compile("player_aaaa=(.*?)</script>").matcher(detailPageDoc.html());
|
||||||
|
String json = matcher.find() ? matcher.group(1) : "";
|
||||||
|
org.json.JSONObject player = new JSONObject(json);
|
||||||
|
String url = player.getString("url");
|
||||||
|
shareLinks.add(url);
|
||||||
|
}
|
||||||
|
quarkUrls = super.detailContentVodPlayUrl(shareLinks);
|
||||||
|
quarkNames = super.detailContentVodPlayFrom(shareLinks);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vod.VodPlayBuilder.BuildResult result = builder.build();
|
||||||
|
|
||||||
|
Vod vod = new Vod();
|
||||||
|
vod.setVodId(ids.get(0));
|
||||||
|
vod.setVodPic(siteUrl + pic);
|
||||||
|
vod.setVodName(name);
|
||||||
|
vod.setVodPlayFrom(result.vodPlayFrom + "$$$" + quarkNames);
|
||||||
|
vod.setVodPlayUrl(result.vodPlayUrl + "$$$" + quarkUrls);
|
||||||
|
return Result.string(vod);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String searchContent(String key, boolean quick) throws Exception {
|
||||||
|
List<Vod> list = new ArrayList<>();
|
||||||
|
Document doc = Jsoup.parse(OkHttp.string(siteUrl.concat("/search/-------------.html?wd=").concat(URLEncoder.encode(key)), getHeaders()));
|
||||||
|
for (Element element : doc.select("ul.stui-vodlist > li > div > a")) {
|
||||||
|
|
||||||
|
String pic = element.attr("data-original");
|
||||||
|
String url = element.attr("href");
|
||||||
|
String name = element.attr("title");
|
||||||
|
if (!pic.startsWith("http")) {
|
||||||
|
pic = siteUrl + pic;
|
||||||
|
}
|
||||||
|
String id = url.split("/")[2];
|
||||||
|
list.add(new Vod(id, name, pic));
|
||||||
|
|
||||||
|
}
|
||||||
|
return Result.string(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String playerContent(String flag, String id, List<String> vipFlags) throws Exception {
|
||||||
|
if (flag.contains("quark")) {
|
||||||
|
return super.playerContent(flag, id, vipFlags);
|
||||||
|
}
|
||||||
|
String target = siteUrl.concat("/play/").concat(id);
|
||||||
|
Document doc = Jsoup.parse(OkHttp.string(target));
|
||||||
|
Matcher matcher = Pattern.compile("player_aaaa=(.*?)</script>").matcher(doc.html());
|
||||||
|
String json = matcher.find() ? matcher.group(1) : "";
|
||||||
|
org.json.JSONObject player = new JSONObject(json);
|
||||||
|
String url = player.getString("url");
|
||||||
|
String from = player.getString("from");
|
||||||
|
String next = player.getString("link_next");
|
||||||
|
String vodid = player.getString("id");
|
||||||
|
String nid = player.getString("nid");
|
||||||
|
String paurl = OkHttp.string(siteUrl + "/static/player/" + from + ".js");
|
||||||
|
Matcher matcher1 = Pattern.compile(" src=\"(.*?)'").matcher(paurl);
|
||||||
|
paurl = matcher1.find() ? matcher1.group(1) : "";
|
||||||
|
String purl = paurl + url + "&next=" + next + "&id=" + vodid + "&nid=" + nid;
|
||||||
|
if (!purl.startsWith("http")) {
|
||||||
|
purl = siteUrl.replace("www.", "") + purl;
|
||||||
|
}
|
||||||
|
String playUrl = OkHttp.string(purl, getHeaders(target.replace("www.", "")));
|
||||||
|
|
||||||
|
String realUrl = Util.getVar(playUrl, "vid");
|
||||||
|
|
||||||
|
return Result.get().url(realUrl).header(getHeaders()).string();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,150 @@
|
||||||
|
package com.github.catvod.spider;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import com.github.catvod.bean.Class;
|
||||||
|
import com.github.catvod.bean.Result;
|
||||||
|
import com.github.catvod.bean.Vod;
|
||||||
|
import com.github.catvod.net.OkHttp;
|
||||||
|
import com.github.catvod.utils.Util;
|
||||||
|
import org.jsoup.Jsoup;
|
||||||
|
import org.jsoup.nodes.Document;
|
||||||
|
import org.jsoup.nodes.Element;
|
||||||
|
import org.jsoup.select.Elements;
|
||||||
|
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author zhixc
|
||||||
|
*/
|
||||||
|
public class Mogg extends Cloud {
|
||||||
|
|
||||||
|
private final String siteUrl = "https://woog.nxog.eu.org/";
|
||||||
|
private final Pattern regexCategory = Pattern.compile("index.php/vod/type/id/(\\w+).html");
|
||||||
|
private final Pattern regexPageTotal = Pattern.compile("\\$\\(\"\\.mac_total\"\\)\\.text\\('(\\d+)'\\);");
|
||||||
|
|
||||||
|
private Map<String, String> getHeader() {
|
||||||
|
Map<String, String> header = new HashMap<>();
|
||||||
|
header.put("User-Agent", Util.CHROME);
|
||||||
|
return header;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(Context context, String extend) throws Exception {
|
||||||
|
// JsonObject ext = Json.safeObject(extend);
|
||||||
|
super.init(context, extend);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String homeContent(boolean filter) {
|
||||||
|
List<Class> classes = new ArrayList<>();
|
||||||
|
Document doc = Jsoup.parse(OkHttp.string(siteUrl, getHeader()));
|
||||||
|
Elements elements = doc.select(".nav-link");
|
||||||
|
for (Element e : elements) {
|
||||||
|
Matcher mather = regexCategory.matcher(e.attr("href"));
|
||||||
|
if (mather.find()) {
|
||||||
|
classes.add(new Class(mather.group(1), e.text().trim()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Result.string(classes, parseVodListFromDoc(doc));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String categoryContent(String tid, String pg, boolean filter, HashMap<String, String> extend) {
|
||||||
|
String[] urlParams = new String[]{tid, "", "", "", "", "", "", "", pg, "", "", ""};
|
||||||
|
if (extend != null && extend.size() > 0) {
|
||||||
|
for (String key : extend.keySet()) {
|
||||||
|
urlParams[Integer.parseInt(key)] = extend.get(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Document doc = Jsoup.parse(OkHttp.string(String.format("%s/index.php/vod/show/id/%s/page/%s.html", siteUrl, tid, pg), getHeader()));
|
||||||
|
int page = Integer.parseInt(pg), limit = 72, total = 0;
|
||||||
|
Matcher matcher = regexPageTotal.matcher(doc.html());
|
||||||
|
if (matcher.find()) total = Integer.parseInt(matcher.group(1));
|
||||||
|
int count = total <= limit ? 1 : ((int) Math.ceil(total / (double) limit));
|
||||||
|
return Result.get().vod(parseVodListFromDoc(doc)).page(page, count, limit, total).string();
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Vod> parseVodListFromDoc(Document doc) {
|
||||||
|
List<Vod> list = new ArrayList<>();
|
||||||
|
Elements elements = doc.select(".module-item");
|
||||||
|
for (Element e : elements) {
|
||||||
|
String vodId = e.selectFirst(".video-name a").attr("href");
|
||||||
|
String vodPic = e.selectFirst(".module-item-pic > img").attr("data-src");
|
||||||
|
String vodName = e.selectFirst(".video-name").text();
|
||||||
|
String vodRemarks = e.selectFirst(".module-item-text").text();
|
||||||
|
list.add(new Vod(vodId, vodName, vodPic, vodRemarks));
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String detailContent(List<String> ids) throws Exception {
|
||||||
|
String vodId = ids.get(0);
|
||||||
|
Document doc = Jsoup.parse(OkHttp.string(siteUrl + vodId, getHeader()));
|
||||||
|
|
||||||
|
Vod item = new Vod();
|
||||||
|
item.setVodId(vodId);
|
||||||
|
item.setVodName(doc.selectFirst(".video-info-header > .page-title").text());
|
||||||
|
item.setVodPic(doc.selectFirst(".module-item-pic img").attr("data-src"));
|
||||||
|
item.setVodArea(doc.select(".video-info-header a.tag-link").last().text());
|
||||||
|
item.setTypeName(String.join(",", doc.select(".video-info-header div.tag-link a").eachText()));
|
||||||
|
|
||||||
|
List<String> shareLinks = doc.select(".module-row-text").eachAttr("data-clipboard-text");
|
||||||
|
for (int i = 0; i < shareLinks.size(); i++) {
|
||||||
|
shareLinks.set(i, shareLinks.get(i).trim());
|
||||||
|
//String detailContent = super.detailContent(List.of(shareLinks.get(i)));
|
||||||
|
}
|
||||||
|
item.setVodPlayUrl(super.detailContentVodPlayUrl(shareLinks));
|
||||||
|
item.setVodPlayFrom(super.detailContentVodPlayFrom(shareLinks));
|
||||||
|
|
||||||
|
Elements elements = doc.select(".video-info-item");
|
||||||
|
for (Element e : elements) {
|
||||||
|
String title = e.previousElementSibling().text();
|
||||||
|
if (title.contains("导演")) {
|
||||||
|
item.setVodDirector(String.join(",", e.select("a").eachText()));
|
||||||
|
} else if (title.contains("主演")) {
|
||||||
|
item.setVodActor(String.join(",", e.select("a").eachText()));
|
||||||
|
} else if (title.contains("年代")) {
|
||||||
|
item.setVodYear(e.selectFirst("a").text().trim());
|
||||||
|
} else if (title.contains("备注")) {
|
||||||
|
item.setVodRemarks(e.text().trim());
|
||||||
|
} else if (title.contains("剧情")) {
|
||||||
|
item.setVodContent(e.selectFirst(".sqjj_a").text().replace("[收起部分]", "").trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.string(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String searchContent(String key, boolean quick) throws Exception {
|
||||||
|
return searchContent(key, "1");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String searchContent(String key, boolean quick, String pg) throws Exception {
|
||||||
|
return searchContent(key, pg);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private String searchContent(String key, String pg) {
|
||||||
|
String searchURL = siteUrl + String.format("/index.php/vod/search/page/%s/wd/%s.html", pg,URLEncoder.encode(key));
|
||||||
|
String html = OkHttp.string(searchURL, getHeader());
|
||||||
|
Elements items = Jsoup.parse(html).select(".module-search-item");
|
||||||
|
List<Vod> list = new ArrayList<>();
|
||||||
|
for (Element item : items) {
|
||||||
|
String vodId = item.select(".video-serial").attr("href");
|
||||||
|
String name = item.select(".video-serial").attr("title");
|
||||||
|
String pic = item.select(".module-item-pic > img").attr("data-src");
|
||||||
|
String remark = item.select(".video-tag-icon").text();
|
||||||
|
list.add(new Vod(vodId, name, pic, remark));
|
||||||
|
}
|
||||||
|
return Result.string(list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,202 @@
|
||||||
|
package com.github.catvod.spider;
|
||||||
|
|
||||||
|
import com.github.catvod.bean.Class;
|
||||||
|
import com.github.catvod.bean.Result;
|
||||||
|
import com.github.catvod.bean.Vod;
|
||||||
|
import com.github.catvod.crawler.Spider;
|
||||||
|
import com.github.catvod.net.OkHttp;
|
||||||
|
import com.github.catvod.utils.AESEncryption;
|
||||||
|
import com.github.catvod.utils.Util;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.jsoup.Jsoup;
|
||||||
|
import org.jsoup.nodes.Document;
|
||||||
|
import org.jsoup.nodes.Element;
|
||||||
|
import org.jsoup.select.Elements;
|
||||||
|
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
public class NCat extends Spider {
|
||||||
|
|
||||||
|
private static final String siteUrl = "https://www.ncat3.app";
|
||||||
|
private static final String picUrl = "https://vres.wbadl.cn";
|
||||||
|
private static final String cateUrl = siteUrl + "/show/";
|
||||||
|
private static final String detailUrl = siteUrl + "/detail/";
|
||||||
|
private static final String searchUrl = siteUrl + "/search?k=";
|
||||||
|
private static final String playUrl = siteUrl + "/play/";
|
||||||
|
|
||||||
|
private HashMap<String, String> getHeaders() {
|
||||||
|
HashMap<String, String> headers = new HashMap<>();
|
||||||
|
headers.put("User-Agent", Util.CHROME);
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String homeContent(boolean filter) throws Exception {
|
||||||
|
List<Vod> list = new ArrayList<>();
|
||||||
|
List<Class> classes = new ArrayList<>();
|
||||||
|
String[] typeIdList = {"1", "2", "3", "4", "6"};
|
||||||
|
String[] typeNameList = {"电影", "连续剧", "动漫", "综艺", "短剧"};
|
||||||
|
for (int i = 0; i < typeNameList.length; i++) {
|
||||||
|
classes.add(new Class(typeIdList[i], typeNameList[i]));
|
||||||
|
}
|
||||||
|
Document doc = Jsoup.parse(OkHttp.string(siteUrl, getHeaders()));
|
||||||
|
for (Element element : doc.select("div.module-item")) {
|
||||||
|
try {
|
||||||
|
String pic = element.select("img").last().attr("data-original");
|
||||||
|
String url = element.select("a").attr("href");
|
||||||
|
String name = element.select(".v-item-title").text().replace("可可影视-kekys.com", "").trim();
|
||||||
|
if (!pic.startsWith("http")) {
|
||||||
|
pic = picUrl + pic;
|
||||||
|
}
|
||||||
|
String id = url.split("/")[2];
|
||||||
|
list.add(new Vod(id, name, pic));
|
||||||
|
} catch (Exception e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Result.string(classes, list);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String categoryContent(String tid, String pg, boolean filter, HashMap<String, String> extend) throws Exception {
|
||||||
|
List<Vod> list = new ArrayList<>();
|
||||||
|
String target = cateUrl + tid + "-----3-" + pg + ".html";
|
||||||
|
Document doc = Jsoup.parse(OkHttp.string(target, getHeaders()));
|
||||||
|
for (Element element : doc.select("div.module-item")) {
|
||||||
|
try {
|
||||||
|
String pic = element.select("img").last().attr("data-original");
|
||||||
|
String url = element.select("a").attr("href");
|
||||||
|
String name = element.select(".v-item-title").text().replace("可可影视-kekys.com", "").trim();
|
||||||
|
if (!pic.startsWith("http")) {
|
||||||
|
pic = picUrl + pic;
|
||||||
|
}
|
||||||
|
String id = url.split("/")[2];
|
||||||
|
list.add(new Vod(id, name, pic));
|
||||||
|
} catch (Exception e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Integer total = (Integer.parseInt(pg) + 1) * 20;
|
||||||
|
return Result.string(Integer.parseInt(pg), Integer.parseInt(pg) + 1, 20, total, list);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String detailContent(List<String> ids) throws Exception {
|
||||||
|
Document doc = Jsoup.parse(OkHttp.string(detailUrl.concat(ids.get(0)), getHeaders()));
|
||||||
|
String name = doc.select("div.detail-title strong").text();
|
||||||
|
String pic = doc.select(".detail-pic img").last().attr("data-original");
|
||||||
|
String year = doc.select("a.detail-tags-item").get(0).text();
|
||||||
|
String desc = doc.select("div.detail-desc p").text();
|
||||||
|
|
||||||
|
// 播放源
|
||||||
|
Elements tabs = doc.select("a.source-item");
|
||||||
|
Elements list = doc.select("div.episode-list");
|
||||||
|
String PlayFrom = "";
|
||||||
|
String PlayUrl = "";
|
||||||
|
for (int i = 0; i < tabs.size(); i++) {
|
||||||
|
String tabName = tabs.get(i).select("span").last().text();
|
||||||
|
if (Arrays.asList("超清", "4K(高峰不卡)").contains(tabName)) continue;
|
||||||
|
if (!"".equals(PlayFrom)) {
|
||||||
|
PlayFrom = PlayFrom + "$$$" + tabName;
|
||||||
|
} else {
|
||||||
|
PlayFrom = PlayFrom + tabName;
|
||||||
|
}
|
||||||
|
Elements li = list.get(i).select("a");
|
||||||
|
String liUrl = "";
|
||||||
|
for (int i1 = 0; i1 < li.size(); i1++) {
|
||||||
|
if (!"".equals(liUrl)) {
|
||||||
|
liUrl = liUrl + "#" + li.get(i1).text() + "$" + li.get(i1).attr("href").replace("/play/", "");
|
||||||
|
} else {
|
||||||
|
liUrl = liUrl + li.get(i1).text() + "$" + li.get(i1).attr("href").replace("/play/", "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!"".equals(PlayUrl)) {
|
||||||
|
PlayUrl = PlayUrl + "$$$" + liUrl;
|
||||||
|
} else {
|
||||||
|
PlayUrl = PlayUrl + liUrl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Vod vod = new Vod();
|
||||||
|
vod.setVodId(ids.get(0));
|
||||||
|
vod.setVodPic(picUrl + pic);
|
||||||
|
vod.setVodYear(year);
|
||||||
|
vod.setVodName(name);
|
||||||
|
vod.setVodContent(desc);
|
||||||
|
vod.setVodPlayFrom(PlayFrom);
|
||||||
|
vod.setVodPlayUrl(PlayUrl);
|
||||||
|
return Result.string(vod);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String searchContent(String key, boolean quick) throws Exception {
|
||||||
|
List<Vod> list = new ArrayList<>();
|
||||||
|
Document doc = Jsoup.parse(OkHttp.string(searchUrl.concat(URLEncoder.encode(key)).concat(".html"), getHeaders()));
|
||||||
|
for (Element element : doc.select("a.search-result-item")) {
|
||||||
|
try {
|
||||||
|
String pic = element.select("img").attr("data-original");
|
||||||
|
String url = element.attr("href");
|
||||||
|
String name = element.select(".v-item-title").text().replace("可可影视-kekys.com", "").trim();
|
||||||
|
if (!pic.startsWith("http")) {
|
||||||
|
pic = picUrl + pic;
|
||||||
|
}
|
||||||
|
String id = url.split("/")[2];
|
||||||
|
list.add(new Vod(id, name, pic));
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Result.string(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String playerContent(String flag, String id, List<String> vipFlags) throws Exception {
|
||||||
|
Document doc = Jsoup.parse(OkHttp.string(playUrl.concat(id), getHeaders()));
|
||||||
|
String regex = "window.whatTMDwhatTMDPPPP = '(.*?)'";
|
||||||
|
String playSource = "playSource=\\{(.*?)\\}";
|
||||||
|
Pattern pattern = Pattern.compile(regex);
|
||||||
|
Matcher matcher = pattern.matcher(doc.html());
|
||||||
|
String url = "";
|
||||||
|
if (matcher.find()) {
|
||||||
|
url = matcher.group(1);
|
||||||
|
Pattern playSourcePattern = Pattern.compile(playSource);
|
||||||
|
Matcher playSourceMatcher = playSourcePattern.matcher(doc.html());
|
||||||
|
//playSourceMatcher.find();
|
||||||
|
String srcUrl = Util.findByRegex("https?://[^\\s/$.?#].+\\.m3u8", doc.html(), 0);
|
||||||
|
if (StringUtils.isNoneBlank(srcUrl)) {
|
||||||
|
return Result.get().url(srcUrl).header(getHeaders()).string();
|
||||||
|
|
||||||
|
}
|
||||||
|
String js = playSourceMatcher.group(1);
|
||||||
|
|
||||||
|
String regex1 = "KKYS\\['safePlay'\\]\\(\\)\\['url'\\]\\(\"([^\"]+)\"\\)";
|
||||||
|
Pattern pattern1 = Pattern.compile(regex1);
|
||||||
|
String jsSource = Util.unicodeToString(js);
|
||||||
|
Matcher matcher1 = pattern1.matcher(jsSource);
|
||||||
|
String iv = "VNF9aVQF!G*0ux@2hAigUeH3";
|
||||||
|
if (matcher1.find()) {
|
||||||
|
iv = matcher1.group(1);
|
||||||
|
}
|
||||||
|
url = decryptUrl(url, iv);
|
||||||
|
}
|
||||||
|
return Result.get().url(url).header(getHeaders()).string();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String decryptUrl(String encryptedData, String iv) {
|
||||||
|
try {
|
||||||
|
String encryptedKey = "VNF9aVQF!G*0ux@2hAigUeH3";
|
||||||
|
|
||||||
|
return AESEncryption.decrypt(encryptedData, encryptedKey, iv, AESEncryption.ECB_PKCS_7_PADDING);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return "123456";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,455 @@
|
||||||
|
package com.github.catvod.spider;
|
||||||
|
|
||||||
|
|
||||||
|
import com.github.catvod.bean.Class;
|
||||||
|
import com.github.catvod.bean.Filter;
|
||||||
|
import com.github.catvod.bean.Result;
|
||||||
|
import com.github.catvod.bean.Vod;
|
||||||
|
import com.github.catvod.crawler.Spider;
|
||||||
|
import com.github.catvod.crawler.SpiderDebug;
|
||||||
|
import com.github.catvod.net.OkHttp;
|
||||||
|
import com.github.catvod.utils.Json;
|
||||||
|
import com.github.catvod.utils.ProxyVideo;
|
||||||
|
import com.github.catvod.utils.Util;
|
||||||
|
import com.google.gson.JsonArray;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonParser;
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class NG extends Spider {
|
||||||
|
private final String FIND_VIDEO_VOD_LIST = "/api.php/provide/vod_list";
|
||||||
|
private final String FIND_CLASSIFICATION = "/api.php/provide/home_nav";
|
||||||
|
private final String VIDEO_DETAIL = "/api.php/provide/vod_detail";
|
||||||
|
private final String SEARCH_SEARCH = "/api.php/provide/search_result";
|
||||||
|
private final List<Class> classList = new ArrayList<>();
|
||||||
|
private final LinkedHashMap<String, List<Filter>> filters = new LinkedHashMap<>();
|
||||||
|
private final String COMMON_URL = Util.base64Decode("aHR0cDovL3lzLmNoYW5nbWVuZ3l1bi5jb20=");
|
||||||
|
|
||||||
|
private Map<String, String> getParams() {
|
||||||
|
Map<String, String> hashMap = new HashMap<>();
|
||||||
|
hashMap.put("devices", "android");
|
||||||
|
hashMap.put("deviceModel", "ASUS_I003DD");
|
||||||
|
hashMap.put("deviceBrand", "ASUS");
|
||||||
|
hashMap.put("deviceVersion", "9");
|
||||||
|
hashMap.put("deviceScreen", "2340*1080");
|
||||||
|
hashMap.put("appVersionCode", "9");
|
||||||
|
hashMap.put("appVersionName", "1.0.9");
|
||||||
|
hashMap.put("time", String.valueOf(System.currentTimeMillis() / 1000));
|
||||||
|
hashMap.put("imei", "");
|
||||||
|
hashMap.put("app", "ylys");
|
||||||
|
return hashMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> getHeaders() {
|
||||||
|
Map<String, String> headers = new HashMap<>();
|
||||||
|
long currentTimeMillis = System.currentTimeMillis();
|
||||||
|
headers.put("timeMillis", String.valueOf(currentTimeMillis));
|
||||||
|
headers.put("sign", Util.MD5(Util.base64Decode("I3VCRnN6ZEVNMG9MMEpSbkA=") + currentTimeMillis));
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String homeContent(boolean filter) {
|
||||||
|
if (classList.isEmpty()) {
|
||||||
|
String string = OkHttp.string(COMMON_URL + FIND_CLASSIFICATION, getParams(), getHeaders());
|
||||||
|
|
||||||
|
JsonArray filterList = JsonParser.parseString(string).getAsJsonArray();
|
||||||
|
for (int index = 0; index < filterList.size(); index++) {
|
||||||
|
if (index == 0) continue;
|
||||||
|
JsonObject obj = filterList.get(index).getAsJsonObject();
|
||||||
|
int id = obj.get("id").getAsInt();
|
||||||
|
String name = obj.get("name").getAsString();
|
||||||
|
Class clazz = new Class(String.valueOf(id), name);
|
||||||
|
classList.add(clazz);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Map<String, String> params = new HashMap<>(getParams());
|
||||||
|
/* for (String s : extend.keySet()) {
|
||||||
|
params.put(s, URLEncoder.encode(extend.get(s), "UTF-8"));
|
||||||
|
}*/
|
||||||
|
params.put("page", "1");
|
||||||
|
params.put("id", classList.get(0).getTypeId());
|
||||||
|
String string = OkHttp.string(COMMON_URL + FIND_VIDEO_VOD_LIST, params, getHeaders());
|
||||||
|
Type type = new TypeToken<Rst<It>>() {
|
||||||
|
}.getType();
|
||||||
|
Rst<It> resp = Json.parseSafe(string, type);
|
||||||
|
List<Vod> vodList = new ArrayList<>();
|
||||||
|
if (resp != null && resp.isSuccess()) {
|
||||||
|
for (It it : resp.getList()) {
|
||||||
|
vodList.add(it.toVod());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
SpiderDebug.log("ng cate error: " + string);
|
||||||
|
}
|
||||||
|
return Result.string(classList, vodList, filters);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String categoryContent(String tid, String pg, boolean filter, HashMap<String, String> extend) {
|
||||||
|
try {
|
||||||
|
Map<String, String> params = new HashMap<>(getParams());
|
||||||
|
/* for (String s : extend.keySet()) {
|
||||||
|
params.put(s, URLEncoder.encode(extend.get(s), "UTF-8"));
|
||||||
|
}*/
|
||||||
|
params.put("page", pg);
|
||||||
|
params.put("id", tid);
|
||||||
|
String string = OkHttp.string(COMMON_URL + FIND_VIDEO_VOD_LIST, params, getHeaders());
|
||||||
|
Type type = new TypeToken<Rst<It>>() {
|
||||||
|
}.getType();
|
||||||
|
Rst<It> resp = Json.parseSafe(string, type);
|
||||||
|
List<Vod> vodList = new ArrayList<>();
|
||||||
|
if (resp != null && resp.isSuccess()) {
|
||||||
|
for (It it : resp.getList()) {
|
||||||
|
vodList.add(it.toVod());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
SpiderDebug.log("ng cate error: " + string);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.string(Integer.parseInt(pg), Integer.parseInt(pg) + 1, vodList.size(), Integer.MAX_VALUE, vodList);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String detailContent(List<String> ids) {
|
||||||
|
Map<String, String> map = new HashMap<>(getParams());
|
||||||
|
map.put("id", ids.get(0));
|
||||||
|
String string = OkHttp.string(COMMON_URL + VIDEO_DETAIL, map, getHeaders());
|
||||||
|
Type type = new TypeToken<Rst<Dt>>() {
|
||||||
|
}.getType();
|
||||||
|
Rst<Dt> dt = Json.parseSafe(string, type);
|
||||||
|
if (!dt.isSuccess()) {
|
||||||
|
SpiderDebug.log("ng detail err: " + dt.getMsg());
|
||||||
|
return Result.error(dt.getMsg());
|
||||||
|
}
|
||||||
|
return Result.string(dt.data.toVod());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String playerContent(String flag, String id, List<String> vipFlags) {
|
||||||
|
String string = OkHttp.string(id);
|
||||||
|
Type type = new TypeToken<Rst<PlayRst>>() {
|
||||||
|
}.getType();
|
||||||
|
Rst<PlayRst> rst = Json.parseSafe(string, type);
|
||||||
|
if (!rst.isSuccess()) {
|
||||||
|
SpiderDebug.log("play err: " + rst.getMsg());
|
||||||
|
return Result.error(rst.getMsg());
|
||||||
|
}
|
||||||
|
Map<String, String> filter = new HashMap<>();
|
||||||
|
|
||||||
|
for (String s : rst.getData().getHeader().keySet()) {
|
||||||
|
if (s.equals("User-Agent")) {
|
||||||
|
filter.put(s, rst.getData().getHeader().get(s));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.get().url(ProxyVideo.buildCommonProxyUrl(rst.getData().getUrl(), filter)).string();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String searchContent(String key, boolean quick) throws UnsupportedEncodingException {
|
||||||
|
Map<String, String> params = new HashMap<>(getParams());
|
||||||
|
params.put("video_name", URLEncoder.encode(key, "UTF-8"));
|
||||||
|
String string = OkHttp.string(COMMON_URL + SEARCH_SEARCH, params, getHeaders());
|
||||||
|
Type type = new TypeToken<Rst<List<SearchRst>>>() {
|
||||||
|
}.getType();
|
||||||
|
Rst<List<SearchRst>> rst = Json.parseSafe(string, type);
|
||||||
|
if (!rst.isSuccess()) {
|
||||||
|
SpiderDebug.log("ng search error: " + rst.getMsg());
|
||||||
|
return Result.error(rst.getMsg());
|
||||||
|
}
|
||||||
|
return Result.string(rst.getData().get(0).toVodList());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Rst<T> {
|
||||||
|
private int code;
|
||||||
|
private String msg;
|
||||||
|
private String limit;
|
||||||
|
private int pagecount;
|
||||||
|
private int total;
|
||||||
|
private List<T> list;
|
||||||
|
private T data;
|
||||||
|
|
||||||
|
public boolean isSuccess() {
|
||||||
|
return code == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCode() {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCode(int code) {
|
||||||
|
this.code = code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMsg() {
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMsg(String msg) {
|
||||||
|
this.msg = msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLimit() {
|
||||||
|
return limit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLimit(String limit) {
|
||||||
|
this.limit = limit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPagecount() {
|
||||||
|
return pagecount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPagecount(int pagecount) {
|
||||||
|
this.pagecount = pagecount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTotal() {
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTotal(int total) {
|
||||||
|
this.total = total;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<T> getList() {
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setList(List<T> list) {
|
||||||
|
this.list = list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T getData() {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setData(T data) {
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class It {
|
||||||
|
private int id;
|
||||||
|
private String img;
|
||||||
|
private String name;
|
||||||
|
private String score;
|
||||||
|
private String msg;
|
||||||
|
|
||||||
|
public Vod toVod() {
|
||||||
|
Vod vod = new Vod();
|
||||||
|
vod.setVodId(String.valueOf(id));
|
||||||
|
vod.setVodName(name);
|
||||||
|
vod.setVodRemarks(score);
|
||||||
|
vod.setVodPic(img);
|
||||||
|
return vod;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Dt {
|
||||||
|
private String name;
|
||||||
|
private String year;
|
||||||
|
private String score;
|
||||||
|
private int hits;
|
||||||
|
private String type;
|
||||||
|
private String img;
|
||||||
|
private String info;
|
||||||
|
@SerializedName("total_count")
|
||||||
|
private int totalCount;
|
||||||
|
@SerializedName("player_info")
|
||||||
|
private List<DtIt> playerInfo;
|
||||||
|
|
||||||
|
public Vod toVod() {
|
||||||
|
Vod vod = new Vod();
|
||||||
|
vod.setVodId(name + score);
|
||||||
|
vod.setVodName(name);
|
||||||
|
vod.setVodPic(img);
|
||||||
|
vod.setVodTag(type);
|
||||||
|
vod.setVodRemarks(year);
|
||||||
|
vod.setVodContent(info);
|
||||||
|
|
||||||
|
StringBuilder playFrom = new StringBuilder();
|
||||||
|
StringBuilder playUrl = new StringBuilder();
|
||||||
|
|
||||||
|
for (DtIt info : playerInfo) {
|
||||||
|
playFrom.append(info.getShow()).append("$$$");
|
||||||
|
for (VtInfo vtInfo : info.getVideoInfo()) {
|
||||||
|
|
||||||
|
playUrl.append(vtInfo.getName()).append("$").append(vtInfo.getUrl().get(0));
|
||||||
|
playUrl.append("#");
|
||||||
|
|
||||||
|
}
|
||||||
|
playUrl.append("$$$");
|
||||||
|
}
|
||||||
|
|
||||||
|
vod.setVodPlayFrom(playFrom.toString());
|
||||||
|
vod.setVodPlayUrl(playUrl.toString());
|
||||||
|
return vod;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class DtIt {
|
||||||
|
private int id;
|
||||||
|
private String from;
|
||||||
|
private String show;
|
||||||
|
@SerializedName("url_count")
|
||||||
|
private int urlCount;
|
||||||
|
@SerializedName("video_info")
|
||||||
|
private List<VtInfo> videoInfo;
|
||||||
|
|
||||||
|
public int getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(int id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFrom() {
|
||||||
|
return from;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFrom(String from) {
|
||||||
|
this.from = from;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getShow() {
|
||||||
|
return show;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setShow(String show) {
|
||||||
|
this.show = show;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getUrlCount() {
|
||||||
|
return urlCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUrlCount(int urlCount) {
|
||||||
|
this.urlCount = urlCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<VtInfo> getVideoInfo() {
|
||||||
|
return videoInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVideoInfo(List<VtInfo> videoInfo) {
|
||||||
|
this.videoInfo = videoInfo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class VtInfo {
|
||||||
|
private int id;
|
||||||
|
private String name;
|
||||||
|
private String pic;
|
||||||
|
private List<String> url;
|
||||||
|
|
||||||
|
public int getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(int id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPic() {
|
||||||
|
return pic;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPic(String pic) {
|
||||||
|
this.pic = pic;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getUrl() {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUrl(List<String> url) {
|
||||||
|
this.url = url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class PlayRst {
|
||||||
|
private String url;
|
||||||
|
private Map<String, String> header;
|
||||||
|
|
||||||
|
public String getUrl() {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUrl(String url) {
|
||||||
|
this.url = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> getHeader() {
|
||||||
|
return header;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHeader(Map<String, String> header) {
|
||||||
|
this.header = header;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class SearchRst {
|
||||||
|
private int id;
|
||||||
|
private String name;
|
||||||
|
private List<SearchRstItem> data;
|
||||||
|
|
||||||
|
public List<Vod> toVodList() {
|
||||||
|
List<Vod> list = new ArrayList<>();
|
||||||
|
for (SearchRstItem datum : data) {
|
||||||
|
list.add(datum.toVOd());
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class SearchRstItem {
|
||||||
|
private int id;
|
||||||
|
private int type;
|
||||||
|
@SerializedName("video_name")
|
||||||
|
private String videoName;
|
||||||
|
private String qingxidu;
|
||||||
|
private String img;
|
||||||
|
private String director;
|
||||||
|
@SerializedName("main_actor")
|
||||||
|
private String mainActor;
|
||||||
|
private String category;
|
||||||
|
|
||||||
|
public Vod toVOd() {
|
||||||
|
Vod vod = new Vod();
|
||||||
|
vod.setVodId(String.valueOf(id));
|
||||||
|
vod.setVodTag(qingxidu);
|
||||||
|
vod.setVodPic(img);
|
||||||
|
vod.setVodRemarks(category);
|
||||||
|
vod.setVodName(videoName);
|
||||||
|
vod.setVodActor(mainActor);
|
||||||
|
return vod;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,277 @@
|
||||||
|
package com.github.catvod.spider
|
||||||
|
|
||||||
|
|
||||||
|
import com.github.catvod.bean.Class
|
||||||
|
import com.github.catvod.bean.Filter
|
||||||
|
import com.github.catvod.bean.Result
|
||||||
|
import com.github.catvod.bean.Vod
|
||||||
|
import com.github.catvod.crawler.Spider
|
||||||
|
import com.github.catvod.crawler.SpiderDebug
|
||||||
|
import com.github.catvod.net.OkHttp
|
||||||
|
import com.github.catvod.utils.Json
|
||||||
|
import com.github.catvod.utils.ProxyVideo
|
||||||
|
import com.github.catvod.utils.Util
|
||||||
|
import com.google.gson.JsonParser
|
||||||
|
import com.google.gson.annotations.SerializedName
|
||||||
|
import com.google.gson.reflect.TypeToken
|
||||||
|
import java.net.URLEncoder
|
||||||
|
|
||||||
|
|
||||||
|
class NGkt : Spider() {
|
||||||
|
private var COMMON_URL: String = Util.base64Decode("aHR0cDovL3lzLmNoYW5nbWVuZ3l1bi5jb20=")
|
||||||
|
private val FIND_VIDEO_VOD_LIST: String = "/api.php/provide/vod_list"
|
||||||
|
private val FIND_CLASSIFICATION: String = "/api.php/provide/home_nav"
|
||||||
|
private val VIDEO_DETAIL: String = "/api.php/provide/vod_detail"
|
||||||
|
private val SEARCH_SEARCH: String = "/api.php/provide/search_result"
|
||||||
|
|
||||||
|
|
||||||
|
private val classList = mutableListOf<Class>()
|
||||||
|
private val filters = LinkedHashMap<String, MutableList<Filter>>()
|
||||||
|
|
||||||
|
|
||||||
|
private fun getParams(): Map<String, String> {
|
||||||
|
val hashMap: HashMap<String, String> = java.util.HashMap()
|
||||||
|
hashMap["devices"] = "android"
|
||||||
|
hashMap["deviceModel"] = "ASUS_I003DD"
|
||||||
|
hashMap["deviceBrand"] = "ASUS"
|
||||||
|
hashMap["deviceVersion"] = "9"
|
||||||
|
hashMap["deviceScreen"] = "2340*1080"
|
||||||
|
hashMap["appVersionCode"] = "9"
|
||||||
|
hashMap["appVersionName"] = "1.0.9"
|
||||||
|
hashMap["time"] = (System.currentTimeMillis() / 1000).toString()
|
||||||
|
hashMap["imei"] = ""
|
||||||
|
hashMap["app"] = "ylys"
|
||||||
|
return hashMap
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getHeaders(): Map<String, String> {
|
||||||
|
val headers = HashMap<String, String>()
|
||||||
|
val currentTimeMillis = System.currentTimeMillis()
|
||||||
|
headers.put("timeMillis", currentTimeMillis.toString())
|
||||||
|
headers.put(
|
||||||
|
"sign", Util.MD5(Util.base64Decode("I3VCRnN6ZEVNMG9MMEpSbkA=") + "$currentTimeMillis")
|
||||||
|
)
|
||||||
|
return headers
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun homeContent(filter: Boolean): String {
|
||||||
|
if (classList.isEmpty()) {
|
||||||
|
val string = OkHttp.string("$COMMON_URL$FIND_CLASSIFICATION", getParams(), getHeaders())
|
||||||
|
val filterList = JsonParser.parseString(string).asJsonArray
|
||||||
|
for ((index, jsonElement) in filterList.withIndex()) {
|
||||||
|
if (index == 0) continue
|
||||||
|
val obj = jsonElement.asJsonObject
|
||||||
|
val id = obj.get("id").asInt
|
||||||
|
val name = obj.get("name").asString
|
||||||
|
val clazz = Class(id.toString(), name)
|
||||||
|
classList.add(clazz)
|
||||||
|
// val msgs = obj.get("msg").asJsonArray
|
||||||
|
// if(msgs.isEmpty){
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
// for (msg in msgs) {
|
||||||
|
// val msgObj = msg.asJsonObject
|
||||||
|
// val dataArray = msgObj.get("data").asJsonArray
|
||||||
|
// val list = mutableListOf<Filter.Value>()
|
||||||
|
// for ((index, jsonElement1) in dataArray.withIndex()) {
|
||||||
|
// if(index == 0) continue
|
||||||
|
// list.add(Filter.Value(jsonElement1.asString, jsonElement1.asString))
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if(filters[id.toString()].isNullOrEmpty()){
|
||||||
|
// filters[id.toString()] = mutableListOf(Filter(msgObj.get("name").asString, dataArray.get(0).asString, list))
|
||||||
|
// }else{
|
||||||
|
// filters[id.toString()]!!.add(Filter(msgObj.get("name").asString, dataArray.get(0).asString, list))
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Result.string(classList, filters)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun categoryContent(
|
||||||
|
tid: String, pg: String, filter: Boolean, extend: java.util.HashMap<String, String>
|
||||||
|
): String {
|
||||||
|
val params = getParams().toMutableMap()
|
||||||
|
extend.forEach { (t, u) ->
|
||||||
|
params[t] = URLEncoder.encode(u, "UTF-8")
|
||||||
|
}
|
||||||
|
params["page"] = pg
|
||||||
|
params["id"] = tid
|
||||||
|
val string = OkHttp.string("$COMMON_URL$FIND_VIDEO_VOD_LIST", params, getHeaders())
|
||||||
|
val type = object : TypeToken<Rst<It>>() {}.type
|
||||||
|
val resp = Json.parseSafe<Rst<It>>(string, type)
|
||||||
|
var vodList = listOf<Vod>()
|
||||||
|
if (resp.isSuccess()) {
|
||||||
|
vodList = resp.list.toVodList()
|
||||||
|
} else {
|
||||||
|
SpiderDebug.log("ng cate error: $string")
|
||||||
|
}
|
||||||
|
return Result.string(classList, vodList, filters)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun detailContent(ids: MutableList<String>): String {
|
||||||
|
val map = getParams().toMutableMap()
|
||||||
|
map["id"] = ids[0]
|
||||||
|
val string = OkHttp.string("$COMMON_URL$VIDEO_DETAIL", map, getHeaders())
|
||||||
|
val type = object : TypeToken<Rst<Dt>>() {}.type
|
||||||
|
val dt = Json.parseSafe<Rst<Dt>>(string, type)
|
||||||
|
if (!dt.isSuccess()) {
|
||||||
|
SpiderDebug.log("ng detail err: ${dt.msg}")
|
||||||
|
return Result.error(dt.msg)
|
||||||
|
}
|
||||||
|
return Result.string(dt.data.toVod().apply { setVodId(ids[0]) })
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun playerContent(flag: String, id: String, vipFlags: MutableList<String>): String {
|
||||||
|
val string = OkHttp.string(id)
|
||||||
|
val type = object : TypeToken<Rst<PlayRst>>() {}.type
|
||||||
|
val rst = Json.parseSafe<Rst<PlayRst>>(string, type)
|
||||||
|
if (!rst.isSuccess()) {
|
||||||
|
SpiderDebug.log("play err: ${rst.msg}")
|
||||||
|
return Result.error(rst.msg)
|
||||||
|
}
|
||||||
|
val filter = rst.data.header.filter { it.key.equals("User-Agent", true) }
|
||||||
|
return Result.get().url(ProxyVideo.buildCommonProxyUrl(rst.data.url, filter)).string()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun searchContent(key: String, quick: Boolean): String {
|
||||||
|
val params = getParams().toMutableMap()
|
||||||
|
params["video_name"] = URLEncoder.encode(key, "UTF-8")
|
||||||
|
val string = OkHttp.string("$COMMON_URL$SEARCH_SEARCH", params, getHeaders())
|
||||||
|
val type = object : TypeToken<Rst<List<SearchRst>>>() {}.type
|
||||||
|
val rst = Json.parseSafe<Rst<List<SearchRst>>>(string, type)
|
||||||
|
if (!rst.isSuccess()) {
|
||||||
|
SpiderDebug.log("ng search error:${rst.msg}")
|
||||||
|
return Result.error(rst.msg)
|
||||||
|
}
|
||||||
|
return Result.string(rst.data[0].toVodList())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
data class Rst<T>(
|
||||||
|
val code: Int,
|
||||||
|
val msg: String,
|
||||||
|
val limit: String,
|
||||||
|
val pagecount: Int,
|
||||||
|
val total: Int,
|
||||||
|
val list: List<T>,
|
||||||
|
val data: T
|
||||||
|
) {
|
||||||
|
fun isSuccess(): Boolean {
|
||||||
|
return code == 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
data class It(
|
||||||
|
val id: Int,
|
||||||
|
val img: String,
|
||||||
|
val name: String,
|
||||||
|
val score: String,
|
||||||
|
val msg: String,
|
||||||
|
) {
|
||||||
|
fun toVod(): Vod {
|
||||||
|
val vod = Vod()
|
||||||
|
vod.setVodId(id.toString())
|
||||||
|
vod.setVodName(name)
|
||||||
|
vod.setVodRemarks(score)
|
||||||
|
vod.setVodPic(img)
|
||||||
|
return vod
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class Dt(
|
||||||
|
val name: String,
|
||||||
|
val year: String,
|
||||||
|
val score: String,
|
||||||
|
val hits: Int,
|
||||||
|
val type: String,
|
||||||
|
val img: String,
|
||||||
|
val info: String,
|
||||||
|
@SerializedName("total_count") val totalCount: Int,
|
||||||
|
@SerializedName("player_info") val playerInfo: List<DtIt>
|
||||||
|
) {
|
||||||
|
fun toVod(): Vod {
|
||||||
|
val vod = Vod()
|
||||||
|
vod.setVodId(name + score)
|
||||||
|
vod.setVodName(name)
|
||||||
|
vod.setVodPic(img)
|
||||||
|
vod.setVodTag(type)
|
||||||
|
vod.setVodRemarks(year)
|
||||||
|
vod.vodContent = info
|
||||||
|
val playFrom = StringBuilder()
|
||||||
|
val playUrl = StringBuilder()
|
||||||
|
for ((_, _, show, _, videoInfo) in playerInfo) {
|
||||||
|
playFrom.append(show).append("$$$")
|
||||||
|
for ((_, name1, _, url) in videoInfo) {
|
||||||
|
playUrl.append(name1).append("$").append(url[0])
|
||||||
|
playUrl.append("#")
|
||||||
|
}
|
||||||
|
playUrl.append("$$$")
|
||||||
|
}
|
||||||
|
vod.setVodPlayFrom(playFrom.toString())
|
||||||
|
vod.vodPlayUrl = playUrl.toString()
|
||||||
|
return vod
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class DtIt(
|
||||||
|
val id: Int,
|
||||||
|
val from: String,
|
||||||
|
val show: String,
|
||||||
|
@SerializedName("url_count") val urlCount: Int,
|
||||||
|
@SerializedName("video_info") val videoInfo: List<VtInfo>
|
||||||
|
)
|
||||||
|
|
||||||
|
data class VtInfo(
|
||||||
|
val id: Int, val name: String, val pic: String, val url: List<String>
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun List<It>.toVodList(): MutableList<Vod> {
|
||||||
|
val list = mutableListOf<Vod>()
|
||||||
|
for (it in this) {
|
||||||
|
list.add(it.toVod())
|
||||||
|
}
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
data class PlayRst(
|
||||||
|
val url: String, val header: Map<String, String>
|
||||||
|
)
|
||||||
|
|
||||||
|
data class SearchRst(
|
||||||
|
val id: Int, val name: String, val data: List<SearchRstItem>
|
||||||
|
) {
|
||||||
|
fun toVodList(): MutableList<Vod> {
|
||||||
|
val list = mutableListOf<Vod>()
|
||||||
|
for (datum in data) {
|
||||||
|
list.add(datum.toVOd())
|
||||||
|
}
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class SearchRstItem(
|
||||||
|
val id: Int,
|
||||||
|
val type: Int,
|
||||||
|
@SerializedName("video_name") val videoName: String,
|
||||||
|
val qingxidu: String,
|
||||||
|
val img: String,
|
||||||
|
val director: String,
|
||||||
|
@SerializedName("main_actor") val mainActor: String,
|
||||||
|
val category: String
|
||||||
|
) {
|
||||||
|
fun toVOd(): Vod {
|
||||||
|
val vod = Vod()
|
||||||
|
vod.setVodId(id.toString())
|
||||||
|
vod.setVodTag(qingxidu)
|
||||||
|
vod.setVodPic(img)
|
||||||
|
vod.setVodRemarks(category)
|
||||||
|
vod.setVodName(videoName)
|
||||||
|
vod.setVodActor(mainActor)
|
||||||
|
return vod
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,154 @@
|
||||||
|
package com.github.catvod.spider;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import com.github.catvod.bean.Class;
|
||||||
|
import com.github.catvod.bean.Result;
|
||||||
|
import com.github.catvod.bean.Vod;
|
||||||
|
import com.github.catvod.net.OkHttp;
|
||||||
|
import com.github.catvod.utils.Json;
|
||||||
|
import com.github.catvod.utils.Util;
|
||||||
|
import com.google.gson.JsonArray;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import org.jsoup.Jsoup;
|
||||||
|
import org.jsoup.nodes.Document;
|
||||||
|
import org.jsoup.nodes.Element;
|
||||||
|
import org.jsoup.select.Elements;
|
||||||
|
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author zhixc
|
||||||
|
*/
|
||||||
|
public class NaBi extends Cloud {
|
||||||
|
|
||||||
|
private String siteUrl = "https://duopan.fun/";
|
||||||
|
private final Pattern regexCategory = Pattern.compile("index.php/vod/type/id/(\\w+).html");
|
||||||
|
private final Pattern regexPageTotal = Pattern.compile("\\$\\(\"\\.mac_total\"\\)\\.text\\('(\\d+)'\\);");
|
||||||
|
|
||||||
|
private Map<String, String> getHeader() {
|
||||||
|
Map<String, String> header = new HashMap<>();
|
||||||
|
header.put("User-Agent", Util.CHROME);
|
||||||
|
return header;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(Context context, String extend) throws Exception {
|
||||||
|
// JsonObject ext = Json.safeObject(extend);
|
||||||
|
super.init(context, extend);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String homeContent(boolean filter) {
|
||||||
|
List<Class> classes = new ArrayList<>();
|
||||||
|
Document doc = Jsoup.parse(OkHttp.string(siteUrl, getHeader()));
|
||||||
|
Elements elements = doc.select(".nav-link");
|
||||||
|
for (Element e : elements) {
|
||||||
|
Matcher mather = regexCategory.matcher(e.attr("href"));
|
||||||
|
if (mather.find()) {
|
||||||
|
classes.add(new Class(mather.group(1), e.text().trim()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Result.string(classes, parseVodListFromDoc(doc));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String categoryContent(String tid, String pg, boolean filter, HashMap<String, String> extend) {
|
||||||
|
String[] urlParams = new String[]{tid, "", "", "", "", "", "", "", pg, "", "", ""};
|
||||||
|
if (extend != null && extend.size() > 0) {
|
||||||
|
for (String key : extend.keySet()) {
|
||||||
|
urlParams[Integer.parseInt(key)] = extend.get(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Document doc = Jsoup.parse(OkHttp.string(String.format("%s/index.php/vod/show/id/%s/page/%s.html", siteUrl, tid, pg), getHeader()));
|
||||||
|
int page = Integer.parseInt(pg), limit = 72, total = 0;
|
||||||
|
Matcher matcher = regexPageTotal.matcher(doc.html());
|
||||||
|
if (matcher.find()) total = Integer.parseInt(matcher.group(1));
|
||||||
|
int count = total <= limit ? 1 : ((int) Math.ceil(total / (double) limit));
|
||||||
|
return Result.get().vod(parseVodListFromDoc(doc)).page(page, count, limit, total).string();
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Vod> parseVodListFromDoc(Document doc) {
|
||||||
|
List<Vod> list = new ArrayList<>();
|
||||||
|
Elements elements = doc.select(".module-item");
|
||||||
|
for (Element e : elements) {
|
||||||
|
String vodId = e.selectFirst(".video-name a").attr("href");
|
||||||
|
String vodPic = e.selectFirst(".module-item-pic > img").attr("data-src");
|
||||||
|
String vodName = e.selectFirst(".video-name").text();
|
||||||
|
String vodRemarks = e.selectFirst(".module-item-text").text();
|
||||||
|
list.add(new Vod(vodId, vodName, vodPic, vodRemarks));
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String detailContent(List<String> ids) throws Exception {
|
||||||
|
String vodId = ids.get(0);
|
||||||
|
Document doc = Jsoup.parse(OkHttp.string(siteUrl + vodId, getHeader()));
|
||||||
|
|
||||||
|
Vod item = new Vod();
|
||||||
|
item.setVodId(vodId);
|
||||||
|
item.setVodName(doc.selectFirst(".video-info-header > .page-title").text());
|
||||||
|
item.setVodPic(doc.selectFirst(".module-item-pic img").attr("data-src"));
|
||||||
|
item.setVodArea(doc.select(".video-info-header a.tag-link").last().text());
|
||||||
|
item.setTypeName(String.join(",", doc.select(".video-info-header div.tag-link a").eachText()));
|
||||||
|
|
||||||
|
List<String> shareLinks = doc.select(".module-row-text").eachAttr("data-clipboard-text");
|
||||||
|
for (int i = 0; i < shareLinks.size(); i++) {
|
||||||
|
shareLinks.set(i, shareLinks.get(i).trim());
|
||||||
|
//String detailContent = super.detailContent(List.of(shareLinks.get(i)));
|
||||||
|
}
|
||||||
|
item.setVodPlayUrl(super.detailContentVodPlayUrl(shareLinks));
|
||||||
|
item.setVodPlayFrom(super.detailContentVodPlayFrom(shareLinks));
|
||||||
|
|
||||||
|
Elements elements = doc.select(".video-info-item");
|
||||||
|
for (Element e : elements) {
|
||||||
|
String title = e.previousElementSibling().text();
|
||||||
|
if (title.contains("导演")) {
|
||||||
|
item.setVodDirector(String.join(",", e.select("a").eachText()));
|
||||||
|
} else if (title.contains("主演")) {
|
||||||
|
item.setVodActor(String.join(",", e.select("a").eachText()));
|
||||||
|
} else if (title.contains("年代")) {
|
||||||
|
item.setVodYear(e.selectFirst("a").text().trim());
|
||||||
|
} else if (title.contains("备注")) {
|
||||||
|
item.setVodRemarks(e.text().trim());
|
||||||
|
} else if (title.contains("剧情")) {
|
||||||
|
item.setVodContent(e.selectFirst(".sqjj_a").text().replace("[收起部分]", "").trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.string(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String searchContent(String key, boolean quick) throws Exception {
|
||||||
|
return searchContent(key, "1");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String searchContent(String key, boolean quick, String pg) throws Exception {
|
||||||
|
return searchContent(key, pg);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private String searchContent(String key, String pg) {
|
||||||
|
String searchURL = siteUrl + String.format("/index.php/vod/search/page/%s/wd/%s.html", pg,URLEncoder.encode(key));
|
||||||
|
String html = OkHttp.string(searchURL, getHeader());
|
||||||
|
Elements items = Jsoup.parse(html).select(".module-search-item");
|
||||||
|
List<Vod> list = new ArrayList<>();
|
||||||
|
for (Element item : items) {
|
||||||
|
String vodId = item.select(".video-serial").attr("href");
|
||||||
|
String name = item.select(".video-serial").attr("title");
|
||||||
|
String pic = item.select(".module-item-pic > img").attr("data-src");
|
||||||
|
String remark = item.select(".video-tag-icon").text();
|
||||||
|
list.add(new Vod(vodId, name, pic, remark));
|
||||||
|
}
|
||||||
|
return Result.string(list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,90 @@
|
||||||
|
package com.github.catvod.spider;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import com.github.catvod.api.Pan123Api;
|
||||||
|
import com.github.catvod.bean.Result;
|
||||||
|
import com.github.catvod.crawler.Spider;
|
||||||
|
import com.github.catvod.crawler.SpiderDebug;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author lushunming
|
||||||
|
*/
|
||||||
|
public class Pan123 extends Spider {
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(Context context, String extend) throws Exception {
|
||||||
|
|
||||||
|
if (StringUtils.isNoneBlank(extend)) {
|
||||||
|
// Pan123Api.INSTANCE.setAuth(extend);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String detailContent(List<String> ids) throws Exception {
|
||||||
|
|
||||||
|
@NotNull Map<@NotNull String, @NotNull String> shareData = Pan123Api.INSTANCE.getShareData(ids.get(0));
|
||||||
|
return Result.string(Pan123Api.INSTANCE.getVod(shareData.get("key"), shareData.get("sharePwd")));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String playerContent(String flag, String id, List<String> vipFlags) throws Exception {
|
||||||
|
return Pan123Api.INSTANCE.playerContent(id, flag);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 獲取詳情內容視頻播放來源(多 shared_link)
|
||||||
|
*
|
||||||
|
* @param ids share_link 集合
|
||||||
|
* @param
|
||||||
|
* @return 詳情內容視頻播放來源
|
||||||
|
*/
|
||||||
|
public String detailContentVodPlayFrom(List<String> ids, int index) {
|
||||||
|
List<String> playFrom = new ArrayList<>();
|
||||||
|
/* if (ids.size() < 2){
|
||||||
|
return TextUtils.join("$$$", Pan123Api.INSTANCE.getPlayFormatList());
|
||||||
|
}*/
|
||||||
|
|
||||||
|
for (int i = 1; i <= ids.size(); i++) {
|
||||||
|
|
||||||
|
for (String s : Pan123Api.INSTANCE.getPlayFormatList()) {
|
||||||
|
playFrom.add(String.format(Locale.getDefault(), "pan123" + s + "#%02d_%02d", i, index));
|
||||||
|
|
||||||
|
}
|
||||||
|
// playFrom.add("天意" + i + index);
|
||||||
|
}
|
||||||
|
return TextUtils.join("$$$", playFrom);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 獲取詳情內容視頻播放地址(多 share_link)
|
||||||
|
*
|
||||||
|
* @param ids share_link 集合
|
||||||
|
* @return 詳情內容視頻播放地址
|
||||||
|
*/
|
||||||
|
public String detailContentVodPlayUrl(List<String> ids){
|
||||||
|
List<String> playUrl = new ArrayList<>();
|
||||||
|
for (String id : ids) {
|
||||||
|
@NotNull Map<@NotNull String, @NotNull String> shareData = Pan123Api.INSTANCE.getShareData(id);
|
||||||
|
try {
|
||||||
|
playUrl.add(Pan123Api.INSTANCE.getVod(shareData.get("key"), shareData.get("sharePwd")).getVodPlayUrl());
|
||||||
|
} catch (Exception e) {
|
||||||
|
SpiderDebug.log("获取播放地址出错:" + e.getMessage());
|
||||||
|
playUrl.add("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return TextUtils.join("$$$", playUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -10,6 +10,7 @@ import org.json.JSONObject;
|
||||||
import org.jsoup.Jsoup;
|
import org.jsoup.Jsoup;
|
||||||
|
|
||||||
import java.net.URLEncoder;
|
import java.net.URLEncoder;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
@ -18,7 +19,7 @@ import java.util.Map;
|
||||||
/**
|
/**
|
||||||
* @author zhixc
|
* @author zhixc
|
||||||
*/
|
*/
|
||||||
public class PanSearch extends Ali {
|
public class PanSearch extends Cloud {
|
||||||
|
|
||||||
private final String URL = "https://www.pansearch.me/";
|
private final String URL = "https://www.pansearch.me/";
|
||||||
|
|
||||||
|
|
@ -40,21 +41,26 @@ public class PanSearch extends Ali {
|
||||||
String html = OkHttp.string(URL, getHeader());
|
String html = OkHttp.string(URL, getHeader());
|
||||||
String data = Jsoup.parse(html).select("script[id=__NEXT_DATA__]").get(0).data();
|
String data = Jsoup.parse(html).select("script[id=__NEXT_DATA__]").get(0).data();
|
||||||
String buildId = new JSONObject(data).getString("buildId");
|
String buildId = new JSONObject(data).getString("buildId");
|
||||||
String url = URL + "_next/data/" + buildId + "/search.json?keyword=" + URLEncoder.encode(key) + "&pan=aliyundrive";
|
List<String> types = List.of("quark", "aliyundrive");
|
||||||
String result = OkHttp.string(url, getSearchHeader());
|
|
||||||
JSONArray array = new JSONObject(result).getJSONObject("pageProps").getJSONObject("data").getJSONArray("data");
|
|
||||||
List<Vod> list = new ArrayList<>();
|
List<Vod> list = new ArrayList<>();
|
||||||
for (int i = 0; i < array.length(); i++) {
|
|
||||||
JSONObject item = array.getJSONObject(i);
|
for (String type : types) {
|
||||||
String content = item.optString("content");
|
String url = URL + "_next/data/" + buildId + "/search.json?keyword=" + URLEncoder.encode(key, Charset.defaultCharset().name()) + "&pan=" + type;
|
||||||
String[] split = content.split("\\n");
|
String result = OkHttp.string(url, getSearchHeader());
|
||||||
if (split.length == 0) continue;
|
JSONArray array = new JSONObject(result).getJSONObject("pageProps").getJSONObject("data").getJSONArray("data");
|
||||||
String vodId = Jsoup.parse(content).select("a").attr("href");
|
for (int i = 0; i < array.length(); i++) {
|
||||||
String name = split[0].replaceAll("</?[^>]+>", "");
|
JSONObject item = array.getJSONObject(i);
|
||||||
String remark = item.optString("time");
|
String content = item.optString("content");
|
||||||
String pic = item.optString("image");
|
String[] split = content.split("\\n");
|
||||||
list.add(new Vod(vodId, name, pic, remark));
|
if (split.length == 0) continue;
|
||||||
|
String vodId = Jsoup.parse(content).select("a").attr("href");
|
||||||
|
String name = split[0].replaceAll("</?[^>]+>", "");
|
||||||
|
String remark = item.optString("time");
|
||||||
|
String pic = item.optString("image");
|
||||||
|
list.add(new Vod(vodId, name, pic, remark));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Result.string(list);
|
return Result.string(list);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,156 @@
|
||||||
|
package com.github.catvod.spider;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import com.github.catvod.bean.Class;
|
||||||
|
import com.github.catvod.bean.Result;
|
||||||
|
import com.github.catvod.bean.Vod;
|
||||||
|
import com.github.catvod.net.OkHttp;
|
||||||
|
import com.github.catvod.utils.Util;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.jsoup.Jsoup;
|
||||||
|
import org.jsoup.nodes.Document;
|
||||||
|
import org.jsoup.nodes.Element;
|
||||||
|
import org.jsoup.select.Elements;
|
||||||
|
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
public class PanTa extends Cloud {
|
||||||
|
private static final String HOST = "https://www.91panta.cn/";
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(Context context, String extend) throws Exception {
|
||||||
|
// JsonObject ext = Json.safeObject(extend);
|
||||||
|
super.init(context, extend);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String homeContent(boolean filter) {
|
||||||
|
|
||||||
|
|
||||||
|
List<Class> classes = new ArrayList<>();
|
||||||
|
Document doc = Jsoup.parse(OkHttp.string(HOST));
|
||||||
|
Elements elements = doc.select("#tabNavigation > a.tab");
|
||||||
|
for (Element element : elements) {
|
||||||
|
|
||||||
|
classes.add(new Class(element.attr("href"), element.text().trim()));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return Result.string(classes, parseVodListFromDoc(doc));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String categoryContent(String tid, String pg, boolean filter, HashMap<String, String> extend) {
|
||||||
|
String url = HOST + tid + "&page=" + pg;
|
||||||
|
Document doc = Jsoup.parse(OkHttp.string(url));
|
||||||
|
int page = Integer.parseInt(pg), limit = 30, total = Integer.MAX_VALUE;
|
||||||
|
return Result.get().vod(parseVodListFromDoc(doc)).page(page, 99999, limit, total).string();
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Vod> parseVodListFromDoc(Document doc) {
|
||||||
|
List<Vod> list = new ArrayList<>();
|
||||||
|
Elements elements = doc.select(".topicList > .topicItem");
|
||||||
|
for (Element e : elements) {
|
||||||
|
String pic = Objects.isNull(e.selectFirst(".tm-m-photos-thumb li")) ? "" : e.selectFirst(".tm-m-photos-thumb li").attr("data-src");
|
||||||
|
pic = StringUtils.isAllBlank(pic) ? e.selectFirst("a.avatarLink img").attr("src") : pic;
|
||||||
|
Element content = e.selectFirst(".content > h2 > a");
|
||||||
|
String vodId = content.attr("href");
|
||||||
|
String vodPic = HOST + pic;
|
||||||
|
String vodName = content.text();
|
||||||
|
|
||||||
|
list.add(new Vod(vodId, vodName, vodPic));
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
// 获取视频信息
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String detailContent(List<String> ids) throws Exception {
|
||||||
|
String vodId = ids.get(0);
|
||||||
|
Document doc = Jsoup.parse(OkHttp.string(HOST + vodId));
|
||||||
|
|
||||||
|
Vod item = new Vod();
|
||||||
|
|
||||||
|
Element titleElement = doc.selectFirst(".title");
|
||||||
|
if (titleElement != null) {
|
||||||
|
item.setVodName(titleElement.text().trim());
|
||||||
|
}
|
||||||
|
item.setVodId("/" + vodId);
|
||||||
|
|
||||||
|
|
||||||
|
// 解析链接
|
||||||
|
String contentHtml = doc.selectFirst(".topicContent").html();
|
||||||
|
String link = null;
|
||||||
|
|
||||||
|
// 第一种匹配模式:<a href>
|
||||||
|
Pattern aPattern = Pattern.compile("<a\\s+(?:[^>]*?\\s+)?href=[\"'](https://caiyun\\.139\\.com/[^\"']*)[\"'][^>]*>", Pattern.CASE_INSENSITIVE);
|
||||||
|
Matcher aMatcher = aPattern.matcher(contentHtml);
|
||||||
|
if (aMatcher.find()) {
|
||||||
|
link = aMatcher.group(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 第二种匹配模式:<span>中的文本
|
||||||
|
if (StringUtils.isAllBlank(link)) {
|
||||||
|
Pattern spanPattern = Pattern.compile("<span\\s+style=\"color:\\s*#0070C0;\\s*\">(https://caiyun\\.139\\.com/[^<]*)</span>", Pattern.CASE_INSENSITIVE);
|
||||||
|
Matcher spanMatcher = spanPattern.matcher(contentHtml);
|
||||||
|
if (spanMatcher.find()) {
|
||||||
|
link = spanMatcher.group(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 第三种匹配模式:纯文本
|
||||||
|
if (StringUtils.isAllBlank(link)) {
|
||||||
|
Pattern textPattern = Pattern.compile("https://caiyun\\.139\\.com/[^<]*");
|
||||||
|
Matcher textMatcher = textPattern.matcher(contentHtml);
|
||||||
|
if (textMatcher.find()) {
|
||||||
|
link = textMatcher.group();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
item.setVodPlayUrl(super.detailContentVodPlayUrl(List.of(link)));
|
||||||
|
item.setVodPlayFrom(super.detailContentVodPlayFrom(List.of(link)));
|
||||||
|
|
||||||
|
String text = doc.select("div.topicContent > p").text().trim();
|
||||||
|
|
||||||
|
String director = Util.findByRegex("导演:(.*)主演", text, 1);
|
||||||
|
String actor = Util.findByRegex("主演:(.*)类型", text, 1);
|
||||||
|
String cat = Util.findByRegex("类型:(.*)制片", text, 1);
|
||||||
|
String area = Util.findByRegex("地区:(.*)语言", text, 1);
|
||||||
|
String year = Util.findByRegex("上映日期:(.*)片长", text, 1);
|
||||||
|
String remark = Util.findByRegex("简介:(.*)", text, 1);
|
||||||
|
item.setVodDirector(director);
|
||||||
|
item.setVodActor(actor);
|
||||||
|
item.setTypeName(cat);
|
||||||
|
item.setVodArea(area);
|
||||||
|
item.setVodYear(year);
|
||||||
|
item.setVodContent(remark);
|
||||||
|
return Result.string(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String searchContent(String key, boolean quick, String pg) throws Exception {
|
||||||
|
return searchContent(key, pg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String searchContent(String key, boolean quick) throws Exception {
|
||||||
|
return searchContent(key, "1");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private String searchContent(String key, String pg) {
|
||||||
|
String searchURL = HOST + String.format("search?keyword=%s&page=%s", URLEncoder.encode(key), pg);
|
||||||
|
String html = OkHttp.string(searchURL);
|
||||||
|
return Result.string(parseVodListFromDoc(Jsoup.parse(html)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,8 +3,14 @@ package com.github.catvod.spider;
|
||||||
import com.github.catvod.crawler.Spider;
|
import com.github.catvod.crawler.Spider;
|
||||||
import com.github.catvod.crawler.SpiderDebug;
|
import com.github.catvod.crawler.SpiderDebug;
|
||||||
import com.github.catvod.net.OkHttp;
|
import com.github.catvod.net.OkHttp;
|
||||||
|
import com.github.catvod.utils.ProxyVideo;
|
||||||
|
import com.github.catvod.utils.Util;
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public class Proxy extends Spider {
|
public class Proxy extends Spider {
|
||||||
|
|
@ -17,17 +23,42 @@ public class Proxy extends Spider {
|
||||||
return new Object[]{200, "text/plain; charset=utf-8", new ByteArrayInputStream("ok".getBytes("UTF-8"))};
|
return new Object[]{200, "text/plain; charset=utf-8", new ByteArrayInputStream("ok".getBytes("UTF-8"))};
|
||||||
case "ali":
|
case "ali":
|
||||||
return Ali.proxy(params);
|
return Ali.proxy(params);
|
||||||
|
case "quark":
|
||||||
|
return Quark.proxy(params);
|
||||||
|
case "uc":
|
||||||
|
return UC.proxy(params);
|
||||||
case "bili":
|
case "bili":
|
||||||
return Bili.proxy(params);
|
return Bili.proxy(params);
|
||||||
case "webdav":
|
case "webdav":
|
||||||
return WebDAV.vod(params);
|
return WebDAV.vod(params);
|
||||||
case "local":
|
case "local":
|
||||||
return Local.proxy(params);
|
return Local.proxy(params);
|
||||||
|
case "proxy":
|
||||||
|
return commonProxy(params);
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final List<String> keys = Arrays.asList("url", "header", "do", "Content-Type", "User-Agent", "Host");
|
||||||
|
|
||||||
|
private static Object[] commonProxy(Map<String, String> params) throws Exception {
|
||||||
|
String url = Util.base64Decode(params.get("url"));
|
||||||
|
Map<String, String> header = new Gson().fromJson(Util.base64Decode(params.get("header")), Map.class);
|
||||||
|
if (header == null) header = new HashMap<>();
|
||||||
|
List<String> keys = Arrays.asList("range", "connection", "accept-encoding");
|
||||||
|
for (String key : params.keySet()) {
|
||||||
|
if (keys.contains(key.toLowerCase())) {
|
||||||
|
header.put(key, params.get(key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*for (Map.Entry<String, String> entry : params.entrySet()) {
|
||||||
|
if (!keys.contains(entry.getKey())) header.put(entry.getKey(), entry.getValue());
|
||||||
|
}*/
|
||||||
|
return ProxyVideo.proxyMultiThread(url, header);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void adjustPort() {
|
static void adjustPort() {
|
||||||
if (Proxy.port > 0) return;
|
if (Proxy.port > 0) return;
|
||||||
int port = 9978;
|
int port = 9978;
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import com.github.catvod.bean.Result;
|
||||||
import com.github.catvod.bean.Sub;
|
import com.github.catvod.bean.Sub;
|
||||||
import com.github.catvod.bean.Vod;
|
import com.github.catvod.bean.Vod;
|
||||||
import com.github.catvod.crawler.Spider;
|
import com.github.catvod.crawler.Spider;
|
||||||
|
import com.github.catvod.crawler.SpiderDebug;
|
||||||
import com.github.catvod.net.OkHttp;
|
import com.github.catvod.net.OkHttp;
|
||||||
import com.github.catvod.utils.Image;
|
import com.github.catvod.utils.Image;
|
||||||
import com.github.catvod.utils.Util;
|
import com.github.catvod.utils.Util;
|
||||||
|
|
@ -19,31 +20,54 @@ import java.util.List;
|
||||||
|
|
||||||
public class Push extends Spider {
|
public class Push extends Spider {
|
||||||
|
|
||||||
private final Ali ali;
|
private Cloud cloud;
|
||||||
|
|
||||||
public Push() {
|
public Push() {
|
||||||
ali = new Ali();
|
cloud = new Cloud();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init(Context context, String extend) {
|
public void init(Context context, String extend) {
|
||||||
ali.init(context, extend);
|
try {
|
||||||
|
cloud.init(context, extend);
|
||||||
|
} catch (Exception e) {
|
||||||
|
SpiderDebug.log("Cloud init error: " + e.getMessage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String detailContent(List<String> ids) throws Exception {
|
public String detailContent(List<String> ids) throws Exception {
|
||||||
if (Ali.pattern.matcher(ids.get(0)).find()) return ali.detailContent(ids);
|
String url = ids.get(0);
|
||||||
return Result.string(vod(ids.get(0)));
|
|
||||||
|
// 使用Cloud类处理各种云盘链接
|
||||||
|
String cloudResult = cloud.detailContent(ids);
|
||||||
|
if (cloudResult != null) {
|
||||||
|
return cloudResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果不是云盘链接,返回普通链接处理结果
|
||||||
|
return Result.string(vod(url));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String playerContent(String flag, String id, List<String> vipFlags) {
|
public String playerContent(String flag, String id, List<String> vipFlags) {
|
||||||
|
try {
|
||||||
|
// 使用Cloud类处理云盘链接的播放
|
||||||
|
String cloudResult = cloud.playerContent(flag, id, vipFlags);
|
||||||
|
if (cloudResult != null) {
|
||||||
|
return cloudResult;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
SpiderDebug.log("Cloud playerContent error: " + e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 原有逻辑处理其他类型链接
|
||||||
if (id.startsWith("http") && id.contains("***")) id = id.replace("***", "#");
|
if (id.startsWith("http") && id.contains("***")) id = id.replace("***", "#");
|
||||||
if (flag.equals("直連")) return Result.get().url(id).subs(getSubs(id)).string();
|
if (flag.equals("直連")) return Result.get().url(id).subs(getSubs(id)).string();
|
||||||
if (flag.equals("解析")) return Result.get().parse().jx().url(id).string();
|
if (flag.equals("解析")) return Result.get().parse().jx().url(id).string();
|
||||||
if (flag.equals("嗅探")) return Result.get().parse().url(id).string();
|
if (flag.equals("嗅探")) return Result.get().parse().url(id).string();
|
||||||
if (flag.equals("迅雷")) return Result.get().url(id).string();
|
if (flag.equals("迅雷")) return Result.get().url(id).string();
|
||||||
return ali.playerContent(flag, id, vipFlags);
|
return Result.get().url(id).string();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Vod vod(String url) {
|
private Vod vod(String url) {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,71 @@
|
||||||
|
package com.github.catvod.spider;
|
||||||
|
|
||||||
|
import com.github.catvod.bean.Result;
|
||||||
|
import com.github.catvod.bean.Vod;
|
||||||
|
import com.github.catvod.crawler.SpiderDebug;
|
||||||
|
import com.github.catvod.net.OkHttp;
|
||||||
|
import com.github.catvod.utils.Json;
|
||||||
|
import com.github.catvod.utils.Util;
|
||||||
|
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
import org.jsoup.Jsoup;
|
||||||
|
import org.jsoup.nodes.Document;
|
||||||
|
import org.jsoup.nodes.Element;
|
||||||
|
import org.jsoup.select.Elements;
|
||||||
|
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author zhixc
|
||||||
|
*/
|
||||||
|
public class QiLeSo extends Cloud {
|
||||||
|
|
||||||
|
private final String URL = "https://www.qileso.com/";
|
||||||
|
|
||||||
|
private Map<String, String> getHeader() {
|
||||||
|
Map<String, String> header = new HashMap<>();
|
||||||
|
header.put("User-Agent", Util.CHROME);
|
||||||
|
return header;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String detailContent(List<String> shareUrl) throws Exception {
|
||||||
|
SpiderDebug.log("qileso detail args:" + Json.toJson(shareUrl));
|
||||||
|
|
||||||
|
String html = OkHttp.string(shareUrl.get(0), getHeader());
|
||||||
|
Document doc = Jsoup.parse(html);
|
||||||
|
Element elements = doc.selectFirst("#body > div > div.thread-body > div.thread-content.message.break-all > p > a");
|
||||||
|
SpiderDebug.log("qileso detail shareurl:" + elements.attr("href"));
|
||||||
|
|
||||||
|
|
||||||
|
String result = super.detailContent(List.of(elements.attr("href")));
|
||||||
|
SpiderDebug.log("qileso detail:" + result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String searchContent(String key, boolean quick) throws Exception {
|
||||||
|
String url = URL + "?s=" + URLEncoder.encode(key, Charset.defaultCharset().name());
|
||||||
|
|
||||||
|
String html = OkHttp.string(url, getHeader());
|
||||||
|
Document doc = Jsoup.parse(html);
|
||||||
|
List<Vod> list = new ArrayList<>();
|
||||||
|
|
||||||
|
Elements elements = doc.select(" ul.list-group > li.list-group-item > div.subject > h2 > a");
|
||||||
|
for (Element element : elements) {
|
||||||
|
String id = element.attr("href");
|
||||||
|
String name = element.text();
|
||||||
|
list.add(new Vod(id, name, ""));
|
||||||
|
}
|
||||||
|
|
||||||
|
SpiderDebug.log("qileso searchContent:" + Result.string(list));
|
||||||
|
|
||||||
|
return Result.string(list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,93 @@
|
||||||
|
package com.github.catvod.spider;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
|
||||||
|
import com.github.catvod.api.QuarkApi;
|
||||||
|
import com.github.catvod.bean.Result;
|
||||||
|
import com.github.catvod.bean.quark.ShareData;
|
||||||
|
import com.github.catvod.crawler.Spider;
|
||||||
|
import com.github.catvod.crawler.SpiderDebug;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ColaMint & Adam & FongMi
|
||||||
|
*/
|
||||||
|
public class Quark extends Spider {
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(Context context, String extend) throws Exception {
|
||||||
|
|
||||||
|
QuarkApi.get().setCookie(extend);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String detailContent(List<String> ids) throws Exception {
|
||||||
|
|
||||||
|
ShareData shareData = QuarkApi.get().getShareData(ids.get(0));
|
||||||
|
return Result.string(QuarkApi.get().getVod(shareData));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String playerContent(String flag, String id, List<String> vipFlags) throws Exception {
|
||||||
|
return QuarkApi.get().playerContent(id.split("\\+\\+"), flag);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 獲取詳情內容視頻播放來源(多 shared_link)
|
||||||
|
*
|
||||||
|
* @param ids share_link 集合
|
||||||
|
* @param i
|
||||||
|
* @return 詳情內容視頻播放來源
|
||||||
|
*/
|
||||||
|
public String detailContentVodPlayFrom(List<String> ids, int index) {
|
||||||
|
List<String> playFrom = new ArrayList<>();
|
||||||
|
/* if (ids.size() < 2){
|
||||||
|
return TextUtils.join("$$$", QuarkApi.get().getPlayFormatList());
|
||||||
|
}*/
|
||||||
|
|
||||||
|
for (int i = 1; i <= ids.size(); i++) {
|
||||||
|
playFrom.add(String.format("quark原画#%02d_%02d" ,i , index));
|
||||||
|
for (String s : QuarkApi.get().getPlayFormatList()) {
|
||||||
|
playFrom.add(String.format(Locale.getDefault(), "quark" + s + "#%02d_%02d", i, index));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return TextUtils.join("$$$", playFrom);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 獲取詳情內容視頻播放地址(多 share_link)
|
||||||
|
*
|
||||||
|
* @param ids share_link 集合
|
||||||
|
* @return 詳情內容視頻播放地址
|
||||||
|
*/
|
||||||
|
public String detailContentVodPlayUrl(List<String> ids) {
|
||||||
|
List<String> playUrl = new ArrayList<>();
|
||||||
|
for (String id : ids) {
|
||||||
|
ShareData shareData = QuarkApi.get().getShareData(id);
|
||||||
|
try {
|
||||||
|
playUrl.add(QuarkApi.get().getVod(shareData)==null?"":QuarkApi.get().getVod(shareData).getVodPlayUrl());
|
||||||
|
} catch (Exception e) {
|
||||||
|
SpiderDebug.log("获取播放地址出错:" + e.getMessage());
|
||||||
|
playUrl.add("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return TextUtils.join("$$$", playUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Object[] proxy(Map<String, String> params) throws Exception {
|
||||||
|
String type = params.get("type");
|
||||||
|
if ("video".equals(type)) return QuarkApi.get().proxyVideo(params);
|
||||||
|
//if ("sub".equals(type)) return AliYun.get().proxySub(params);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,192 @@
|
||||||
|
package com.github.catvod.spider;
|
||||||
|
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.util.Pair;
|
||||||
|
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import com.github.catvod.bean.Result;
|
||||||
|
import com.github.catvod.bean.Vod;
|
||||||
|
import com.github.catvod.net.OkHttp;
|
||||||
|
|
||||||
|
import org.jsoup.Jsoup;
|
||||||
|
import org.jsoup.nodes.Document;
|
||||||
|
import org.jsoup.nodes.Element;
|
||||||
|
import org.jsoup.select.Elements;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class Qupans extends Cloud {
|
||||||
|
|
||||||
|
private static final String BASE_URL = "https://www.qupanshe.com";
|
||||||
|
private static final String DEFAULT_COVER_URL = "https://fs-im-kefu.7moor-fs1.com/ly/4d2c3f00-7d4c-11e5-af15-41bf63ae4ea0/1743950734122/baidu.jpg";
|
||||||
|
|
||||||
|
private String get(String url) {
|
||||||
|
Map<String, String> headers = new HashMap<>();
|
||||||
|
headers.put("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36");
|
||||||
|
headers.put("Referer", BASE_URL);
|
||||||
|
return OkHttp.string(url, headers);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String homeContent(boolean filter) {
|
||||||
|
try {
|
||||||
|
JSONObject result = new JSONObject();
|
||||||
|
JSONArray classes = new JSONArray();
|
||||||
|
String[][] types = {{"电影","3"}, {"电视剧","2"}, {"综艺","4"}, {"动漫","5"}, {"纪录片","6"}};
|
||||||
|
for (String[] type : types) {
|
||||||
|
JSONObject cls = new JSONObject();
|
||||||
|
cls.put("type_id", type[1]);
|
||||||
|
cls.put("type_name", type[0]);
|
||||||
|
classes.put(cls);
|
||||||
|
}
|
||||||
|
result.put("class", classes);
|
||||||
|
return result.toString();
|
||||||
|
} catch (Exception e) {
|
||||||
|
return "{\"class\":[]}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String categoryContent(String tid, String pg, boolean filter, HashMap<String, String> extend) {
|
||||||
|
try {
|
||||||
|
String html = get(BASE_URL + "/forum.php?mod=forumdisplay&fid=" + tid + "&page=" + pg);
|
||||||
|
Document doc = Jsoup.parse(html);
|
||||||
|
JSONArray list = new JSONArray();
|
||||||
|
|
||||||
|
for (Element item : doc.select("div.tit_box > a.s")) {
|
||||||
|
String title = item.text();
|
||||||
|
if (title.contains("公告") || title.contains("求")) continue;
|
||||||
|
JSONObject vod = new JSONObject();
|
||||||
|
vod.put("vod_id", item.attr("href"));
|
||||||
|
vod.put("vod_name", title);
|
||||||
|
vod.put("vod_pic", DEFAULT_COVER_URL);
|
||||||
|
vod.put("vod_remarks", "");
|
||||||
|
list.put(vod);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new JSONObject()
|
||||||
|
.put("list", list)
|
||||||
|
.put("page", pg)
|
||||||
|
.put("pagecount", "0")
|
||||||
|
.put("total", "0")
|
||||||
|
.toString();
|
||||||
|
} catch (Exception e) {
|
||||||
|
return "{\"list\":[]}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String detailContent(List<String> ids) {
|
||||||
|
try {
|
||||||
|
String vodId = ids.get(0);
|
||||||
|
String html = get(vodId.startsWith("http") ? vodId : BASE_URL + "/" + vodId);
|
||||||
|
Document doc = Jsoup.parse(html);
|
||||||
|
|
||||||
|
Vod vod = new Vod();
|
||||||
|
vod.setVodId(vodId);
|
||||||
|
vod.setVodName(doc.select("h1").first().text());
|
||||||
|
vod.setVodPic(DEFAULT_COVER_URL);
|
||||||
|
|
||||||
|
List<String> links = getLinks(doc);
|
||||||
|
if (!links.isEmpty()) {
|
||||||
|
String pwd = getPwd(doc);
|
||||||
|
for (int i = 0; i < links.size(); i++) {
|
||||||
|
String link = links.get(i);
|
||||||
|
if (!link.contains("pwd=") && !TextUtils.isEmpty(pwd)) {
|
||||||
|
links.set(i, link + "?pwd=" + pwd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vod.setVodPlayFrom(super.detailContentVodPlayFrom(links));
|
||||||
|
vod.setVodPlayUrl(super.detailContentVodPlayUrl(links));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.string(vod);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Vod vod = new Vod();
|
||||||
|
vod.setVodId(ids.get(0));
|
||||||
|
vod.setVodName("加载失败");
|
||||||
|
vod.setVodPic(DEFAULT_COVER_URL);
|
||||||
|
return Result.string(vod);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> getLinks(Document doc) {
|
||||||
|
List<String> links = new ArrayList<>();
|
||||||
|
// 从a标签中提取链接
|
||||||
|
Elements linksElements = doc.select("a");
|
||||||
|
for (Element linkElement : linksElements) {
|
||||||
|
String href = linkElement.attr("href");
|
||||||
|
if (href.contains(".baidu")) {
|
||||||
|
links.add(href);
|
||||||
|
break; // 只提取第一个百度网盘链接
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return links;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getPwd(Document doc) {
|
||||||
|
try {
|
||||||
|
// 使用正则表达式模式提取密码
|
||||||
|
String patternStr = "提取码:\\s*([A-Za-z0-9]{4})";
|
||||||
|
java.util.regex.Pattern pattern = java.util.regex.Pattern.compile(patternStr);
|
||||||
|
Element contentElement = doc.select("td.t_f").first();
|
||||||
|
if (contentElement != null) {
|
||||||
|
String text = contentElement.text();
|
||||||
|
java.util.regex.Matcher matcher = pattern.matcher(text);
|
||||||
|
if (matcher.find()) {
|
||||||
|
return matcher.group(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 忽略异常
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String searchContent(String key, boolean quick) {
|
||||||
|
return searchContent(key, quick, "1");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String searchContent(String key, boolean quick, String pg) {
|
||||||
|
try {
|
||||||
|
Map<String, String> params = new HashMap<>();
|
||||||
|
params.put("searchsubmit", "yes");
|
||||||
|
params.put("srchtxt", key);
|
||||||
|
String[] fids = {"2", "3", "4", "5", "6"};
|
||||||
|
for (int i = 0; i < fids.length; i++) params.put("srchfid[" + i + "]", fids[i]);
|
||||||
|
|
||||||
|
Map<String, String> headers = new HashMap<>();
|
||||||
|
headers.put("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36");
|
||||||
|
headers.put("content-type", "application/x-www-form-urlencoded");
|
||||||
|
|
||||||
|
String html = OkHttp.post(BASE_URL + "/search.php?mod=forum", params, headers).getBody();
|
||||||
|
Document doc = Jsoup.parse(html);
|
||||||
|
|
||||||
|
JSONArray list = new JSONArray();
|
||||||
|
for (Element item : doc.select("#threadlist ul li h3 > a")) {
|
||||||
|
JSONObject vod = new JSONObject();
|
||||||
|
vod.put("vod_id", item.attr("href"));
|
||||||
|
vod.put("vod_name", item.text());
|
||||||
|
vod.put("vod_pic", DEFAULT_COVER_URL);
|
||||||
|
vod.put("vod_remarks", "");
|
||||||
|
list.put(vod);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new JSONObject()
|
||||||
|
.put("list", list)
|
||||||
|
.put("page", pg)
|
||||||
|
.put("pagecount", "1")
|
||||||
|
.put("total", list.length())
|
||||||
|
.toString();
|
||||||
|
} catch (Exception e) {
|
||||||
|
return "{\"list\":[]}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,195 @@
|
||||||
|
package com.github.catvod.spider;
|
||||||
|
|
||||||
|
import com.github.catvod.bean.Class;
|
||||||
|
import com.github.catvod.bean.Result;
|
||||||
|
import com.github.catvod.bean.Vod;
|
||||||
|
import com.github.catvod.crawler.Spider;
|
||||||
|
import com.github.catvod.net.OkHttp;
|
||||||
|
import com.github.catvod.utils.Util;
|
||||||
|
|
||||||
|
import org.jsoup.Jsoup;
|
||||||
|
import org.jsoup.nodes.Document;
|
||||||
|
import org.jsoup.nodes.Element;
|
||||||
|
import org.jsoup.select.Elements;
|
||||||
|
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
public class QxiTv extends Spider {
|
||||||
|
|
||||||
|
private static final String siteUrl = "https://www.7xi.tv";
|
||||||
|
private static final String cateUrl = siteUrl + "/vodtype/";
|
||||||
|
private static final String searchUrl = siteUrl + "/vodsearch/page/1/wd/";
|
||||||
|
private static final String playUrl = siteUrl + "/FosiPlayer/API.php";
|
||||||
|
|
||||||
|
private HashMap<String, String> getHeaders() {
|
||||||
|
HashMap<String, String> headers = new HashMap<>();
|
||||||
|
headers.put("User-Agent", Util.CHROME);
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String homeContent(boolean filter) throws Exception {
|
||||||
|
List<Vod> list = new ArrayList<>();
|
||||||
|
List<Class> classes = new ArrayList<>();
|
||||||
|
|
||||||
|
Document doc = Jsoup.parse(OkHttp.string(siteUrl, getHeaders()));
|
||||||
|
for (Element element : doc.select("ul.swiper-wrapper > li.swiper-slide > a")) {
|
||||||
|
if(element.text().contains("留言")){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
classes.add(new Class(element.attr("href"),element.text()));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Element element : doc.select("a.public-list-exp")) {
|
||||||
|
try {
|
||||||
|
String pic = element.select("img").attr("data-src");
|
||||||
|
String url = element.attr("href");
|
||||||
|
String name = element.attr("title");
|
||||||
|
if (!pic.startsWith("http")) {
|
||||||
|
pic = siteUrl + pic;
|
||||||
|
}
|
||||||
|
list.add(new Vod(url, name, pic));
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return Result.string(classes, list);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String MD5(String string) {
|
||||||
|
// 创建 MD5 实例
|
||||||
|
try {
|
||||||
|
MessageDigest md = MessageDigest.getInstance("MD5");
|
||||||
|
// 计算 MD5 哈希值
|
||||||
|
byte[] hashBytes = md.digest(string.getBytes());
|
||||||
|
|
||||||
|
// 将字节数组转换为十六进制字符串表示
|
||||||
|
StringBuilder hexString = new StringBuilder();
|
||||||
|
for (byte hashByte : hashBytes) {
|
||||||
|
String hex = Integer.toHexString(0xff & hashByte);
|
||||||
|
if (hex.length() == 1) {
|
||||||
|
hexString.append('0');
|
||||||
|
}
|
||||||
|
hexString.append(hex);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 输出加密后的 MD5 字符串
|
||||||
|
System.out.println("MD5 加密: " + hexString.toString());
|
||||||
|
return hexString.toString();
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String categoryContent(String tid, String pg, boolean filter, HashMap<String, String> extend) throws Exception {
|
||||||
|
String url = siteUrl + tid;
|
||||||
|
if(!Objects.equals(pg, "1")){
|
||||||
|
url += "-" + pg;
|
||||||
|
}
|
||||||
|
String res = OkHttp.string(url, getHeaders());
|
||||||
|
Document document = Jsoup.parse(res);
|
||||||
|
List<Vod> list = new ArrayList<>();
|
||||||
|
Elements vodList = document.select("a.public-list-exp");
|
||||||
|
for (Element element : vodList) {
|
||||||
|
String vodId = element.attr("href");
|
||||||
|
String title = element.attr("title");
|
||||||
|
String img = element.select("img").attr("data-src");
|
||||||
|
String remark = element.select(".public-list-prb").val();
|
||||||
|
list.add(new Vod(vodId, title, img, remark));
|
||||||
|
}
|
||||||
|
|
||||||
|
Integer total = Integer.MAX_VALUE;
|
||||||
|
return Result.string(Integer.parseInt(pg), Integer.parseInt(pg) + 1, list.size(), total, list);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String detailContent(List<String> ids) throws Exception {
|
||||||
|
Document doc = Jsoup.parse(OkHttp.string(siteUrl + ids.get(0), getHeaders()));
|
||||||
|
String name = doc.select("div.this-desc-title").text();
|
||||||
|
String pic = doc.select("div.this-pic-bj").attr("style").replace("background-image: url('", "").replace("')", "");
|
||||||
|
String year = doc.select("div.this-desc-info > span").get(1).text();
|
||||||
|
|
||||||
|
// 播放源
|
||||||
|
Elements tabs = doc.select("a.swiper-slide");
|
||||||
|
Elements list = doc.select("div.anthology-list-box.none");
|
||||||
|
String PlayFrom = "";
|
||||||
|
String PlayUrl = "";
|
||||||
|
for (int i = 0; i < tabs.size(); i++) {
|
||||||
|
String tabName = tabs.get(i).text();
|
||||||
|
if (!"".equals(PlayFrom)) {
|
||||||
|
PlayFrom = PlayFrom + "$$$" + tabName;
|
||||||
|
} else {
|
||||||
|
PlayFrom = PlayFrom + tabName;
|
||||||
|
}
|
||||||
|
Elements li = list.get(i).select("a");
|
||||||
|
String liUrl = "";
|
||||||
|
for (int i1 = 0; i1 < li.size(); i1++) {
|
||||||
|
if (!"".equals(liUrl)){
|
||||||
|
liUrl = liUrl + "#" + li.get(i1).text() + "$" + li.get(i1).attr("href");
|
||||||
|
}else {
|
||||||
|
liUrl = liUrl + li.get(i1).text() + "$" + li.get(i1).attr("href");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!"".equals(PlayUrl)) {
|
||||||
|
PlayUrl = PlayUrl + "$$$" + liUrl;
|
||||||
|
}else {
|
||||||
|
PlayUrl = PlayUrl + liUrl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Vod vod = new Vod();
|
||||||
|
vod.setVodId(ids.get(0));
|
||||||
|
vod.setVodPic(pic);
|
||||||
|
vod.setVodYear(year);
|
||||||
|
vod.setVodName(name);
|
||||||
|
vod.setVodPlayFrom(PlayFrom);
|
||||||
|
vod.setVodPlayUrl(PlayUrl);
|
||||||
|
return Result.string(vod);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String searchContent(String key, boolean quick) throws Exception {
|
||||||
|
List<Vod> list = new ArrayList<>();
|
||||||
|
Document doc = Jsoup.parse(OkHttp.string(searchUrl.concat(URLEncoder.encode(key)).concat(".html"), getHeaders()));
|
||||||
|
for (Element element : doc.select("a.public-list-exp")) {
|
||||||
|
try {
|
||||||
|
String pic = element.select("img").attr("data-src");
|
||||||
|
String url = element.attr("href");
|
||||||
|
String name = element.attr("title");
|
||||||
|
if (!pic.startsWith("http")) {
|
||||||
|
pic = siteUrl + pic;
|
||||||
|
}
|
||||||
|
list.add(new Vod(url, name, pic));
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Result.string(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String playerContent(String flag, String id, List<String> vipFlags) throws Exception {
|
||||||
|
Document doc = Jsoup.parse(OkHttp.string(siteUrl.concat(id),getHeaders()));
|
||||||
|
String regex = "\"url\":\"(.*?)m3u8\",";
|
||||||
|
|
||||||
|
Pattern pattern = Pattern.compile(regex);
|
||||||
|
Matcher matcher = pattern.matcher(doc.html());
|
||||||
|
String url = "";
|
||||||
|
if (matcher.find()) {
|
||||||
|
url = matcher.group(1);
|
||||||
|
url = url.replace("\\/","/") + "m3u8";
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.get().url(url).header(getHeaders()).string();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,75 @@
|
||||||
|
package com.github.catvod.spider
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import com.github.catvod.bean.Class
|
||||||
|
import com.github.catvod.bean.Result
|
||||||
|
import com.github.catvod.bean.Vod
|
||||||
|
import com.github.catvod.net.OkHttp
|
||||||
|
import com.github.catvod.utils.Json
|
||||||
|
import com.github.catvod.utils.Util
|
||||||
|
import org.jsoup.Jsoup
|
||||||
|
import org.jsoup.nodes.Document
|
||||||
|
import java.net.URLEncoder
|
||||||
|
import java.nio.charset.Charset
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 电影云集
|
||||||
|
*
|
||||||
|
* @author lushunming
|
||||||
|
* @createdate 2024-12-03
|
||||||
|
*/
|
||||||
|
class ReBoYingShi : Cloud() {
|
||||||
|
private val siteUrl = "https://reboys.cn"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private val headerWithCookie: MutableMap<String?, String?>
|
||||||
|
get() {
|
||||||
|
val header: MutableMap<String?, String?> = HashMap<String?, String?>()
|
||||||
|
header.put("User-Agent", Util.CHROME)
|
||||||
|
|
||||||
|
return header
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(Exception::class)
|
||||||
|
override fun init(context: Context?, extend: String?) {
|
||||||
|
super.init(context, extend)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Throws(Exception::class)
|
||||||
|
override fun searchContent(key: String?, quick: Boolean): String? {
|
||||||
|
return searchContent(key, "1")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(Exception::class)
|
||||||
|
override fun searchContent(key: String?, quick: Boolean, pg: String?): String? {
|
||||||
|
return searchContent(key, pg)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun searchContent(key: String?, pg: String?): String? {
|
||||||
|
val searchPageURL = siteUrl + "/s/${URLEncoder.encode(key, Charset.defaultCharset().name())}.html"
|
||||||
|
val html = OkHttp.string(searchPageURL, this.headerWithCookie)
|
||||||
|
val apiToken = Util.findByRegex("const apiToken = \"(.*?)\";", html, 1)
|
||||||
|
|
||||||
|
val searchURL = siteUrl + "/search?keyword=${URLEncoder.encode(key, Charset.defaultCharset().name())}"
|
||||||
|
val header = headerWithCookie.toMutableMap()
|
||||||
|
header.put("API-TOKEN", apiToken)
|
||||||
|
val json = OkHttp.string(searchURL, header)
|
||||||
|
val jsonObj = Json.safeObject(json)
|
||||||
|
var vodList = emptyList<Vod>()
|
||||||
|
if (jsonObj.get("code").asInt == 0) {
|
||||||
|
val results = jsonObj.get("data").asJsonObject.get("data").asJsonObject.get("results").asJsonArray
|
||||||
|
vodList = results.map {
|
||||||
|
val title = it.asJsonObject.get("title").asString
|
||||||
|
val vodId = it.asJsonObject.get("links").asJsonArray[0].asJsonObject.get("url").asString
|
||||||
|
Vod(vodId, title, "", "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Result.string(vodList)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,248 @@
|
||||||
|
package com.github.catvod.spider
|
||||||
|
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import com.github.catvod.bean.Class
|
||||||
|
import com.github.catvod.bean.Filter
|
||||||
|
import com.github.catvod.bean.Result
|
||||||
|
import com.github.catvod.bean.Vod
|
||||||
|
import com.github.catvod.net.OkHttp
|
||||||
|
import com.github.catvod.utils.Util
|
||||||
|
import com.google.gson.JsonObject
|
||||||
|
import com.google.gson.JsonParser
|
||||||
|
import kotlinx.coroutines.*
|
||||||
|
import okhttp3.HttpUrl
|
||||||
|
import org.jsoup.Jsoup
|
||||||
|
import org.jsoup.nodes.Document
|
||||||
|
import java.net.URLEncoder
|
||||||
|
import java.util.concurrent.CopyOnWriteArrayList
|
||||||
|
import java.util.regex.Pattern
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author lushunming
|
||||||
|
*/
|
||||||
|
class SeedHub : Cloud() {
|
||||||
|
private val siteUrl = "https://www.seedhub.cc"
|
||||||
|
private val regexCategory: Pattern = Pattern.compile("/vodtype/(\\w+).html")
|
||||||
|
private val regexPageTotal: Pattern = Pattern.compile("\\$\\(\"\\.mac_total\"\\)\\.text\\('(\\d+)'\\);")
|
||||||
|
|
||||||
|
private var extend: JsonObject? = null
|
||||||
|
|
||||||
|
|
||||||
|
private val header: MutableMap<String?, String?>
|
||||||
|
get() {
|
||||||
|
val header: MutableMap<String?, String?> = HashMap<String?, String?>()
|
||||||
|
header.put("User-Agent", Util.MOBILE)
|
||||||
|
return header
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(Exception::class)
|
||||||
|
override fun init(context: Context, extend: String) {
|
||||||
|
this.extend = JsonParser.parseString(extend).getAsJsonObject()
|
||||||
|
super.init(context, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun homeContent(filter: Boolean): String? {
|
||||||
|
val classes: MutableList<Class?> = ArrayList<Class?>()
|
||||||
|
val filters = LinkedHashMap<String?, MutableList<Filter?>?>()
|
||||||
|
|
||||||
|
|
||||||
|
val html = OkHttp.string(siteUrl, this.header)
|
||||||
|
val doc = Jsoup.parse(html)
|
||||||
|
|
||||||
|
parseClassFromDoc(doc, classes)
|
||||||
|
if (filter) {
|
||||||
|
parseFilterFromDoc(doc, filters)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.string(classes, parseVodListFromDoc(doc), filters)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseFilterFromDoc(
|
||||||
|
doc: Document, filters: LinkedHashMap<String?, MutableList<Filter?>?>
|
||||||
|
) {
|
||||||
|
|
||||||
|
val groups = doc.select("div.sidebar-group")
|
||||||
|
for (group in groups) {
|
||||||
|
val clazz = group.select("p.sidebar-heading a")
|
||||||
|
if (clazz.attr("href").startsWith("/")) {
|
||||||
|
val name = clazz.text()
|
||||||
|
val url = clazz.attr("href")
|
||||||
|
val filter = group.select("ul.sidebar-sub-headers>li.sidebar-sub-header>a")
|
||||||
|
val filterList: MutableList<Filter?> = ArrayList<Filter?>()
|
||||||
|
val values: MutableList<Filter.Value> = ArrayList<Filter.Value>()
|
||||||
|
for (f in filter) {
|
||||||
|
val filterName = f.text()
|
||||||
|
val filterUrl = f.attr("href")
|
||||||
|
values.add(Filter.Value(filterName, filterUrl))
|
||||||
|
}
|
||||||
|
filterList.add(Filter("0", "分类", values))
|
||||||
|
filters[url] = filterList
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*获取分类
|
||||||
|
*/
|
||||||
|
private fun parseClassFromDoc(
|
||||||
|
doc: Document, classes: MutableList<Class?>
|
||||||
|
) {
|
||||||
|
val navs = doc.select("div.nav-item")
|
||||||
|
for (nav in navs) {
|
||||||
|
val link = nav.select("a")
|
||||||
|
if (link.attr("href").startsWith("/")) {
|
||||||
|
val name = nav.select("a").text()
|
||||||
|
val url = nav.select("a").attr("href")
|
||||||
|
classes.add(Class(url, name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun categoryContent(
|
||||||
|
tid: String?, pg: String, filter: Boolean, extend: HashMap<String, String?>?
|
||||||
|
): String? {
|
||||||
|
var urlParams = tid
|
||||||
|
if (extend != null && extend.size > 0) {
|
||||||
|
extend.keys.forEach {
|
||||||
|
urlParams = extend[it]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val doc = Jsoup.parse(
|
||||||
|
OkHttp.string(
|
||||||
|
String.format("%s%s?page=%s", siteUrl, urlParams, pg), this.header
|
||||||
|
)
|
||||||
|
)
|
||||||
|
val page = pg.toInt()
|
||||||
|
val limit = 20
|
||||||
|
val total = Int.MAX_VALUE
|
||||||
|
|
||||||
|
|
||||||
|
return Result.get().vod(parseVodListFromDoc(doc)).page(page, 0, limit, total).string()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseVodListFromDoc(doc: Document): MutableList<Vod?> {
|
||||||
|
val list: MutableList<Vod?> = ArrayList<Vod?>()
|
||||||
|
val elements = doc.select("div.cover")
|
||||||
|
for (e in elements) {
|
||||||
|
val vodId = e.selectFirst(" a")!!.attr("href")
|
||||||
|
var vodPic = e.selectFirst("img")!!.attr("src")
|
||||||
|
if (!vodPic.startsWith("http")) {
|
||||||
|
vodPic = siteUrl + vodPic
|
||||||
|
}
|
||||||
|
val vodName = e.selectFirst("h2")!!.text()
|
||||||
|
val vodRemarks = e.select("ul >li")!!.text()
|
||||||
|
list.add(Vod(vodId, vodName, vodPic, vodRemarks))
|
||||||
|
}
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(Exception::class)
|
||||||
|
override fun detailContent(ids: MutableList<String?>): String? {
|
||||||
|
|
||||||
|
val vodId = ids.get(0)
|
||||||
|
val doc = Jsoup.parse(
|
||||||
|
OkHttp.string(
|
||||||
|
siteUrl + vodId, this.header
|
||||||
|
)
|
||||||
|
)
|
||||||
|
val infos = doc.select("div.cover-container >ul > li").text().replace(" ", "")
|
||||||
|
val item = Vod()
|
||||||
|
item.setVodId(vodId)
|
||||||
|
item.setVodName(doc.selectFirst("h1")!!.text())
|
||||||
|
item.setVodPic(doc.selectFirst("div.cover-container img")!!.attr("src"))
|
||||||
|
item.setVodArea(Util.getStrByRegex(Pattern.compile("制片国家/地区:(.*?)语言:"), infos))
|
||||||
|
item.setTypeName(Util.getStrByRegex(Pattern.compile("类型:(.*?)制片"), infos))
|
||||||
|
item.setVodDirector(Util.getStrByRegex(Pattern.compile("导演:(.*?)制片"), infos))
|
||||||
|
item.setVodActor(Util.getStrByRegex(Pattern.compile("主演:(.*?)类型"), infos))
|
||||||
|
item.setVodYear(Util.getStrByRegex(Pattern.compile("首播:(.*?)集数"), infos))
|
||||||
|
item.setVodRemarks(Util.getStrByRegex(Pattern.compile("豆瓣评分:(.*?)常用标签"), infos))
|
||||||
|
item.vodContent = doc.select("div.content > p").text()
|
||||||
|
|
||||||
|
val shareLinks: CopyOnWriteArrayList<String?> = CopyOnWriteArrayList<String?>()
|
||||||
|
val jobs = ArrayList<Job>()
|
||||||
|
|
||||||
|
runBlocking {
|
||||||
|
val docEle = doc.select("ul.pan-links > li > a")
|
||||||
|
docEle.filter { it.attr("data-link").contains("uc") }.take(2).forEach { element ->
|
||||||
|
|
||||||
|
jobs += CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
var link = siteUrl + element.attr("href")
|
||||||
|
val movieTitle = HttpUrl.parse(link)?.queryParameter("movie_title")
|
||||||
|
link = HttpUrl.parse(link)?.newBuilder()?.removeAllQueryParameters("movie_title")
|
||||||
|
?.addEncodedQueryParameter(
|
||||||
|
"movie_title", URLEncoder.encode(movieTitle)
|
||||||
|
)?.build().toString()
|
||||||
|
val string = OkHttp.string(link, header)
|
||||||
|
val docEle = Jsoup.parse(string)
|
||||||
|
docEle.select("a.direct-pan").attr("href").let {
|
||||||
|
if (it.isNotEmpty()) {
|
||||||
|
shareLinks.add(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
docEle.filter { it.attr("data-link").contains("baidu") }.take(2).forEach { element ->
|
||||||
|
|
||||||
|
jobs += CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
var link = siteUrl + element.attr("href")
|
||||||
|
val movieTitle = HttpUrl.parse(link)?.queryParameter("movie_title")
|
||||||
|
link = HttpUrl.parse(link)?.newBuilder()?.removeAllQueryParameters("movie_title")
|
||||||
|
?.addEncodedQueryParameter(
|
||||||
|
"movie_title", URLEncoder.encode(movieTitle)
|
||||||
|
)?.build().toString()
|
||||||
|
val string = OkHttp.string(link, header)
|
||||||
|
val docEle = Jsoup.parse(string)
|
||||||
|
docEle.select("a.direct-pan").attr("href").let {
|
||||||
|
if (it.isNotEmpty()) {
|
||||||
|
shareLinks.add(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
docEle.filter { it.attr("data-link").contains("quark") }.take(2).forEach { element ->
|
||||||
|
|
||||||
|
jobs += CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
var link = siteUrl + element.attr("href")
|
||||||
|
val movieTitle = HttpUrl.parse(link)?.queryParameter("movie_title")
|
||||||
|
link = HttpUrl.parse(link)?.newBuilder()?.removeAllQueryParameters("movie_title")
|
||||||
|
?.addEncodedQueryParameter(
|
||||||
|
"movie_title", URLEncoder.encode(movieTitle)
|
||||||
|
)?.build().toString()
|
||||||
|
val string = OkHttp.string(link, header)
|
||||||
|
val docEle = Jsoup.parse(string)
|
||||||
|
docEle.select("a.direct-pan").attr("href").let {
|
||||||
|
if (it.isNotEmpty()) {
|
||||||
|
shareLinks.add(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
jobs.joinAll()
|
||||||
|
item.vodPlayUrl = super.detailContentVodPlayUrl(java.util.ArrayList(shareLinks))
|
||||||
|
item.setVodPlayFrom(super.detailContentVodPlayFrom(java.util.ArrayList(shareLinks)))
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
return Result.string(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(Exception::class)
|
||||||
|
override fun searchContent(key: String?, quick: Boolean): String? {
|
||||||
|
return searchContent(key, "1")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(Exception::class)
|
||||||
|
override fun searchContent(key: String?, quick: Boolean, pg: String?): String? {
|
||||||
|
return searchContent(key, pg)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun searchContent(key: String?, pg: String?): String? {
|
||||||
|
val searchURL = siteUrl + String.format("/s/%s/?page=%s", URLEncoder.encode(key), pg)
|
||||||
|
val html = OkHttp.string(searchURL, this.header)
|
||||||
|
val doc = Jsoup.parse(html)
|
||||||
|
|
||||||
|
return Result.string(parseVodListFromDoc(doc))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,154 @@
|
||||||
|
package com.github.catvod.spider;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import com.github.catvod.bean.Class;
|
||||||
|
import com.github.catvod.bean.Result;
|
||||||
|
import com.github.catvod.bean.Vod;
|
||||||
|
import com.github.catvod.net.OkHttp;
|
||||||
|
import com.github.catvod.utils.Json;
|
||||||
|
import com.github.catvod.utils.Util;
|
||||||
|
import com.google.gson.JsonArray;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import org.jsoup.Jsoup;
|
||||||
|
import org.jsoup.nodes.Document;
|
||||||
|
import org.jsoup.nodes.Element;
|
||||||
|
import org.jsoup.select.Elements;
|
||||||
|
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author zhixc
|
||||||
|
*/
|
||||||
|
public class ShanDian extends Cloud {
|
||||||
|
|
||||||
|
private String siteUrl = "http://1.95.79.193/";
|
||||||
|
private final Pattern regexCategory = Pattern.compile("index.php/vod/type/id/(\\w+).html");
|
||||||
|
private final Pattern regexPageTotal = Pattern.compile("\\$\\(\"\\.mac_total\"\\)\\.text\\('(\\d+)'\\);");
|
||||||
|
|
||||||
|
private Map<String, String> getHeader() {
|
||||||
|
Map<String, String> header = new HashMap<>();
|
||||||
|
header.put("User-Agent", Util.CHROME);
|
||||||
|
return header;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(Context context, String extend) throws Exception {
|
||||||
|
// JsonObject ext = Json.safeObject(extend);
|
||||||
|
super.init(context, extend);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String homeContent(boolean filter) {
|
||||||
|
List<Class> classes = new ArrayList<>();
|
||||||
|
Document doc = Jsoup.parse(OkHttp.string(siteUrl, getHeader()));
|
||||||
|
Elements elements = doc.select(".nav-link");
|
||||||
|
for (Element e : elements) {
|
||||||
|
Matcher mather = regexCategory.matcher(e.attr("href"));
|
||||||
|
if (mather.find()) {
|
||||||
|
classes.add(new Class(mather.group(1), e.text().trim()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Result.string(classes, parseVodListFromDoc(doc));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String categoryContent(String tid, String pg, boolean filter, HashMap<String, String> extend) {
|
||||||
|
String[] urlParams = new String[]{tid, "", "", "", "", "", "", "", pg, "", "", ""};
|
||||||
|
if (extend != null && extend.size() > 0) {
|
||||||
|
for (String key : extend.keySet()) {
|
||||||
|
urlParams[Integer.parseInt(key)] = extend.get(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Document doc = Jsoup.parse(OkHttp.string(String.format("%s/index.php/vod/show/id/%s/page/%s.html", siteUrl, tid, pg), getHeader()));
|
||||||
|
int page = Integer.parseInt(pg), limit = 72, total = 0;
|
||||||
|
Matcher matcher = regexPageTotal.matcher(doc.html());
|
||||||
|
if (matcher.find()) total = Integer.parseInt(matcher.group(1));
|
||||||
|
int count = total <= limit ? 1 : ((int) Math.ceil(total / (double) limit));
|
||||||
|
return Result.get().vod(parseVodListFromDoc(doc)).page(page, count, limit, total).string();
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Vod> parseVodListFromDoc(Document doc) {
|
||||||
|
List<Vod> list = new ArrayList<>();
|
||||||
|
Elements elements = doc.select(".module-item");
|
||||||
|
for (Element e : elements) {
|
||||||
|
String vodId = e.selectFirst(".video-name a").attr("href");
|
||||||
|
String vodPic = e.selectFirst(".module-item-pic > img").attr("data-src");
|
||||||
|
String vodName = e.selectFirst(".video-name").text();
|
||||||
|
String vodRemarks = e.selectFirst(".module-item-text").text();
|
||||||
|
list.add(new Vod(vodId, vodName, vodPic, vodRemarks));
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String detailContent(List<String> ids) throws Exception {
|
||||||
|
String vodId = ids.get(0);
|
||||||
|
Document doc = Jsoup.parse(OkHttp.string(siteUrl + vodId, getHeader()));
|
||||||
|
|
||||||
|
Vod item = new Vod();
|
||||||
|
item.setVodId(vodId);
|
||||||
|
item.setVodName(doc.selectFirst(".video-info-header > .page-title").text());
|
||||||
|
item.setVodPic(doc.selectFirst(".module-item-pic img").attr("data-src"));
|
||||||
|
item.setVodArea(doc.select(".video-info-header a.tag-link").last().text());
|
||||||
|
item.setTypeName(String.join(",", doc.select(".video-info-header div.tag-link a").eachText()));
|
||||||
|
|
||||||
|
List<String> shareLinks = doc.select(".module-row-text").eachAttr("data-clipboard-text");
|
||||||
|
for (int i = 0; i < shareLinks.size(); i++) {
|
||||||
|
shareLinks.set(i, shareLinks.get(i).trim());
|
||||||
|
//String detailContent = super.detailContent(List.of(shareLinks.get(i)));
|
||||||
|
}
|
||||||
|
item.setVodPlayUrl(super.detailContentVodPlayUrl(shareLinks));
|
||||||
|
item.setVodPlayFrom(super.detailContentVodPlayFrom(shareLinks));
|
||||||
|
|
||||||
|
Elements elements = doc.select(".video-info-item");
|
||||||
|
for (Element e : elements) {
|
||||||
|
String title = e.previousElementSibling().text();
|
||||||
|
if (title.contains("导演")) {
|
||||||
|
item.setVodDirector(String.join(",", e.select("a").eachText()));
|
||||||
|
} else if (title.contains("主演")) {
|
||||||
|
item.setVodActor(String.join(",", e.select("a").eachText()));
|
||||||
|
} else if (title.contains("年代")) {
|
||||||
|
item.setVodYear(e.selectFirst("a").text().trim());
|
||||||
|
} else if (title.contains("备注")) {
|
||||||
|
item.setVodRemarks(e.text().trim());
|
||||||
|
} else if (title.contains("剧情")) {
|
||||||
|
item.setVodContent(e.selectFirst(".sqjj_a").text().replace("[收起部分]", "").trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.string(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String searchContent(String key, boolean quick) throws Exception {
|
||||||
|
return searchContent(key, "1");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String searchContent(String key, boolean quick, String pg) throws Exception {
|
||||||
|
return searchContent(key, pg);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private String searchContent(String key, String pg) {
|
||||||
|
String searchURL = siteUrl + String.format("/index.php/vod/search/page/%s/wd/%s.html", pg,URLEncoder.encode(key));
|
||||||
|
String html = OkHttp.string(searchURL, getHeader());
|
||||||
|
Elements items = Jsoup.parse(html).select(".module-search-item");
|
||||||
|
List<Vod> list = new ArrayList<>();
|
||||||
|
for (Element item : items) {
|
||||||
|
String vodId = item.select(".video-serial").attr("href");
|
||||||
|
String name = item.select(".video-serial").attr("title");
|
||||||
|
String pic = item.select(".module-item-pic > img").attr("data-src");
|
||||||
|
String remark = item.select(".video-tag-icon").text();
|
||||||
|
list.add(new Vod(vodId, name, pic, remark));
|
||||||
|
}
|
||||||
|
return Result.string(list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,154 @@
|
||||||
|
package com.github.catvod.spider;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import com.github.catvod.bean.Class;
|
||||||
|
import com.github.catvod.bean.Result;
|
||||||
|
import com.github.catvod.bean.Vod;
|
||||||
|
import com.github.catvod.net.OkHttp;
|
||||||
|
import com.github.catvod.utils.Json;
|
||||||
|
import com.github.catvod.utils.Util;
|
||||||
|
import com.google.gson.JsonArray;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import org.jsoup.Jsoup;
|
||||||
|
import org.jsoup.nodes.Document;
|
||||||
|
import org.jsoup.nodes.Element;
|
||||||
|
import org.jsoup.select.Elements;
|
||||||
|
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author zhixc
|
||||||
|
*/
|
||||||
|
public class TeXiaFan extends Cloud {
|
||||||
|
|
||||||
|
private String siteUrl = "http://www.xn--ghqy10g1w0a.xyz/";
|
||||||
|
private final Pattern regexCategory = Pattern.compile("index.php/vod/type/id/(\\w+).html");
|
||||||
|
private final Pattern regexPageTotal = Pattern.compile("\\$\\(\"\\.mac_total\"\\)\\.text\\('(\\d+)'\\);");
|
||||||
|
|
||||||
|
private Map<String, String> getHeader() {
|
||||||
|
Map<String, String> header = new HashMap<>();
|
||||||
|
header.put("User-Agent", Util.CHROME);
|
||||||
|
return header;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(Context context, String extend) throws Exception {
|
||||||
|
// JsonObject ext = Json.safeObject(extend);
|
||||||
|
super.init(context, extend);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String homeContent(boolean filter) {
|
||||||
|
List<Class> classes = new ArrayList<>();
|
||||||
|
Document doc = Jsoup.parse(OkHttp.string(siteUrl, getHeader()));
|
||||||
|
Elements elements = doc.select(".nav-link");
|
||||||
|
for (Element e : elements) {
|
||||||
|
Matcher mather = regexCategory.matcher(e.attr("href"));
|
||||||
|
if (mather.find()) {
|
||||||
|
classes.add(new Class(mather.group(1), e.text().trim()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Result.string(classes, parseVodListFromDoc(doc));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String categoryContent(String tid, String pg, boolean filter, HashMap<String, String> extend) {
|
||||||
|
String[] urlParams = new String[]{tid, "", "", "", "", "", "", "", pg, "", "", ""};
|
||||||
|
if (extend != null && extend.size() > 0) {
|
||||||
|
for (String key : extend.keySet()) {
|
||||||
|
urlParams[Integer.parseInt(key)] = extend.get(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Document doc = Jsoup.parse(OkHttp.string(String.format("%s/index.php/vod/show/id/%s/page/%s.html", siteUrl, tid, pg), getHeader()));
|
||||||
|
int page = Integer.parseInt(pg), limit = 72, total = 0;
|
||||||
|
Matcher matcher = regexPageTotal.matcher(doc.html());
|
||||||
|
if (matcher.find()) total = Integer.parseInt(matcher.group(1));
|
||||||
|
int count = total <= limit ? 1 : ((int) Math.ceil(total / (double) limit));
|
||||||
|
return Result.get().vod(parseVodListFromDoc(doc)).page(page, count, limit, total).string();
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Vod> parseVodListFromDoc(Document doc) {
|
||||||
|
List<Vod> list = new ArrayList<>();
|
||||||
|
Elements elements = doc.select(".module-item");
|
||||||
|
for (Element e : elements) {
|
||||||
|
String vodId = e.selectFirst(".video-name a").attr("href");
|
||||||
|
String vodPic = e.selectFirst(".module-item-pic > img").attr("data-src");
|
||||||
|
String vodName = e.selectFirst(".video-name").text();
|
||||||
|
String vodRemarks = e.selectFirst(".module-item-text").text();
|
||||||
|
list.add(new Vod(vodId, vodName, vodPic, vodRemarks));
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String detailContent(List<String> ids) throws Exception {
|
||||||
|
String vodId = ids.get(0);
|
||||||
|
Document doc = Jsoup.parse(OkHttp.string(siteUrl + vodId, getHeader()));
|
||||||
|
|
||||||
|
Vod item = new Vod();
|
||||||
|
item.setVodId(vodId);
|
||||||
|
item.setVodName(doc.selectFirst(".video-info-header > .page-title").text());
|
||||||
|
item.setVodPic(doc.selectFirst(".module-item-pic img").attr("data-src"));
|
||||||
|
item.setVodArea(doc.select(".video-info-header a.tag-link").last().text());
|
||||||
|
item.setTypeName(String.join(",", doc.select(".video-info-header div.tag-link a").eachText()));
|
||||||
|
|
||||||
|
List<String> shareLinks = doc.select(".module-row-text").eachAttr("data-clipboard-text");
|
||||||
|
for (int i = 0; i < shareLinks.size(); i++) {
|
||||||
|
shareLinks.set(i, shareLinks.get(i).trim());
|
||||||
|
//String detailContent = super.detailContent(List.of(shareLinks.get(i)));
|
||||||
|
}
|
||||||
|
item.setVodPlayUrl(super.detailContentVodPlayUrl(shareLinks));
|
||||||
|
item.setVodPlayFrom(super.detailContentVodPlayFrom(shareLinks));
|
||||||
|
|
||||||
|
Elements elements = doc.select(".video-info-item");
|
||||||
|
for (Element e : elements) {
|
||||||
|
String title = e.previousElementSibling().text();
|
||||||
|
if (title.contains("导演")) {
|
||||||
|
item.setVodDirector(String.join(",", e.select("a").eachText()));
|
||||||
|
} else if (title.contains("主演")) {
|
||||||
|
item.setVodActor(String.join(",", e.select("a").eachText()));
|
||||||
|
} else if (title.contains("年代")) {
|
||||||
|
item.setVodYear(e.selectFirst("a").text().trim());
|
||||||
|
} else if (title.contains("备注")) {
|
||||||
|
item.setVodRemarks(e.text().trim());
|
||||||
|
} else if (title.contains("剧情")) {
|
||||||
|
item.setVodContent(e.selectFirst(".sqjj_a").text().replace("[收起部分]", "").trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.string(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String searchContent(String key, boolean quick) throws Exception {
|
||||||
|
return searchContent(key, "1");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String searchContent(String key, boolean quick, String pg) throws Exception {
|
||||||
|
return searchContent(key, pg);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private String searchContent(String key, String pg) {
|
||||||
|
String searchURL = siteUrl + String.format("/index.php/vod/search/page/%s/wd/%s.html", pg,URLEncoder.encode(key));
|
||||||
|
String html = OkHttp.string(searchURL, getHeader());
|
||||||
|
Elements items = Jsoup.parse(html).select(".module-search-item");
|
||||||
|
List<Vod> list = new ArrayList<>();
|
||||||
|
for (Element item : items) {
|
||||||
|
String vodId = item.select(".video-serial").attr("href");
|
||||||
|
String name = item.select(".video-serial").attr("title");
|
||||||
|
String pic = item.select(".module-item-pic > img").attr("data-src");
|
||||||
|
String remark = item.select(".video-tag-icon").text();
|
||||||
|
list.add(new Vod(vodId, name, pic, remark));
|
||||||
|
}
|
||||||
|
return Result.string(list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,56 @@
|
||||||
|
package com.github.catvod.spider
|
||||||
|
|
||||||
|
import com.github.catvod.bean.Result
|
||||||
|
import com.github.catvod.bean.Vod
|
||||||
|
import com.github.catvod.net.OkHttp
|
||||||
|
import com.github.catvod.utils.Json
|
||||||
|
import com.github.catvod.utils.Util
|
||||||
|
import java.net.URLEncoder
|
||||||
|
import java.nio.charset.Charset
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author zhixc
|
||||||
|
*/
|
||||||
|
class Tg123Search : Cloud() {
|
||||||
|
private val URL = "https://tgsou.252035.xyz/"
|
||||||
|
|
||||||
|
private val header: Map<String, String>
|
||||||
|
get() {
|
||||||
|
val header: MutableMap<String, String> = HashMap()
|
||||||
|
header["User-Agent"] = Util.CHROME
|
||||||
|
return header
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Throws(Exception::class)
|
||||||
|
override fun searchContent(key: String, quick: Boolean): String {
|
||||||
|
val url =
|
||||||
|
URL + "?channelUsername=wp123zy,xx123pan,yp123pan,zyfb123&pic=true&keyword=" + URLEncoder.encode(
|
||||||
|
key, Charset.defaultCharset().name()
|
||||||
|
)
|
||||||
|
val list: MutableList<Vod> = ArrayList()
|
||||||
|
val html = OkHttp.string(url, header)
|
||||||
|
val json = Json.safeObject(html);
|
||||||
|
json["results"].asJsonArray.forEach { element ->
|
||||||
|
val array = element.asString.split("$$$")
|
||||||
|
if (array.size >= 2 && array[1].isNotEmpty()) {
|
||||||
|
array[1].split("##").forEach {
|
||||||
|
|
||||||
|
|
||||||
|
val id = it.split("@")[0]
|
||||||
|
val pic = it.split("@")[1].split("$$")[0]
|
||||||
|
val name = it.split("@")[1].split("$$")[1]
|
||||||
|
|
||||||
|
list.add(Vod(id, name, pic, ""))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return Result.string(list)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,56 @@
|
||||||
|
package com.github.catvod.spider
|
||||||
|
|
||||||
|
import com.github.catvod.bean.Result
|
||||||
|
import com.github.catvod.bean.Vod
|
||||||
|
import com.github.catvod.net.OkHttp
|
||||||
|
import com.github.catvod.utils.Json
|
||||||
|
import com.github.catvod.utils.Util
|
||||||
|
import java.net.URLEncoder
|
||||||
|
import java.nio.charset.Charset
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author zhixc
|
||||||
|
*/
|
||||||
|
class Tg189Search : Cloud() {
|
||||||
|
private val URL = "https://tgsou.252035.xyz/"
|
||||||
|
|
||||||
|
private val header: Map<String, String>
|
||||||
|
get() {
|
||||||
|
val header: MutableMap<String, String> = HashMap()
|
||||||
|
header["User-Agent"] = Util.CHROME
|
||||||
|
return header
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Throws(Exception::class)
|
||||||
|
override fun searchContent(key: String, quick: Boolean): String {
|
||||||
|
val url =
|
||||||
|
URL + "?channelUsername=cloud189_group,yunpan139,cloud189_group,cloudtianyi,kuakeclound,tianyirigeng,txtyzy,tyypzhpd,tyysypzypd,yunpan189&pic=true&keyword=" + URLEncoder.encode(
|
||||||
|
key, Charset.defaultCharset().name()
|
||||||
|
)
|
||||||
|
val list: MutableList<Vod> = ArrayList()
|
||||||
|
val html = OkHttp.string(url, header)
|
||||||
|
val json = Json.safeObject(html);
|
||||||
|
json["results"].asJsonArray.forEach { element ->
|
||||||
|
val array = element.asString.split("$$$")
|
||||||
|
if (array.size >= 2 && array[1].isNotEmpty()) {
|
||||||
|
array[1].split("##").forEach {
|
||||||
|
|
||||||
|
|
||||||
|
val id = it.split("@")[0]
|
||||||
|
val pic = it.split("@")[1].split("$$")[0]
|
||||||
|
val name = it.split("@")[1].split("$$")[1]
|
||||||
|
|
||||||
|
list.add(Vod(id, name, pic, ""))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return Result.string(list)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,57 @@
|
||||||
|
package com.github.catvod.spider
|
||||||
|
|
||||||
|
import com.github.catvod.bean.Result
|
||||||
|
import com.github.catvod.bean.Vod
|
||||||
|
import com.github.catvod.net.OkHttp
|
||||||
|
import com.github.catvod.utils.Json
|
||||||
|
import com.github.catvod.utils.Util
|
||||||
|
import java.net.URLEncoder
|
||||||
|
import java.nio.charset.Charset
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author zhixc
|
||||||
|
*/
|
||||||
|
class TgQuarkSearch : Cloud() {
|
||||||
|
private val URL = "https://tgsou.252035.xyz/"
|
||||||
|
|
||||||
|
private val header: Map<String, String>
|
||||||
|
get() {
|
||||||
|
val header: MutableMap<String, String> = HashMap()
|
||||||
|
header["User-Agent"] = Util.CHROME
|
||||||
|
return header
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Throws(Exception::class)
|
||||||
|
override fun searchContent(key: String, quick: Boolean): String {
|
||||||
|
val url =
|
||||||
|
URL + "?channelUsername=alyp_1,clouddriveresources,dianyingshare,hdhhd21,jdjdn1111,leoziyuan,NewQuark,PanjClub,Quark_Movies,xiangxiunb,yunpanchat,yunpanqk,XiangxiuNB,alyp_4K_Movies,alyp_Animation,alyp_TV,alyp_JLP&pic=true&keyword=" + URLEncoder.encode(
|
||||||
|
key, Charset.defaultCharset().name()
|
||||||
|
)
|
||||||
|
val list: MutableList<Vod> = ArrayList()
|
||||||
|
val html = OkHttp.string(url, header)
|
||||||
|
val json = Json.safeObject(html);
|
||||||
|
json["results"].asJsonArray.forEach { element ->
|
||||||
|
val array = element.asString.split("$$$")
|
||||||
|
if (array.size >= 2 && array[1].isNotEmpty()) {
|
||||||
|
array[1].split("##").forEach {
|
||||||
|
|
||||||
|
|
||||||
|
val id = it.split("@")[0]
|
||||||
|
val pic = it.split("@")[1].split("$$")[0]
|
||||||
|
val name = it.split("@")[1].split("$$")[1]
|
||||||
|
|
||||||
|
list.add(Vod(id, name, pic, ""))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return Result.string(list)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
package com.github.catvod.spider
|
||||||
|
|
||||||
|
import com.github.catvod.bean.Result
|
||||||
|
import com.github.catvod.bean.Vod
|
||||||
|
import com.github.catvod.net.OkHttp
|
||||||
|
import com.github.catvod.utils.Util
|
||||||
|
import org.jsoup.Jsoup
|
||||||
|
import java.net.URLEncoder
|
||||||
|
import java.nio.charset.Charset
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author zhixc
|
||||||
|
*/
|
||||||
|
class TgSearch : Cloud() {
|
||||||
|
private val URL = "https://tg.252035.xyz/"
|
||||||
|
|
||||||
|
private val header: Map<String, String>
|
||||||
|
get() {
|
||||||
|
val header: MutableMap<String, String> = HashMap()
|
||||||
|
header["User-Agent"] = Util.CHROME
|
||||||
|
return header
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Throws(Exception::class)
|
||||||
|
override fun searchContent(key: String, quick: Boolean): String {
|
||||||
|
val url =
|
||||||
|
URL + "?channelUsername=tianyirigeng,tyypzhpd,XiangxiuNB,yunpanpan,kuakeyun,zaihuayun,Quark_Movies,alyp_4K_Movies,vip115hot,yunpanshare,dianyingshare&pic=true&keyword=" + URLEncoder.encode(
|
||||||
|
key, Charset.defaultCharset().name()
|
||||||
|
)
|
||||||
|
val list: MutableList<Vod> = ArrayList()
|
||||||
|
val html = OkHttp.string(url, header)
|
||||||
|
val arr = html.split(":I".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
|
||||||
|
if (arr.size > 0) {
|
||||||
|
for (s in arr) {
|
||||||
|
val doc = Jsoup.parse(s)
|
||||||
|
val id = doc.select(" a").eachAttr("href")
|
||||||
|
.first { it.contains("189") || it.contains("139") || it.contains("quark") }
|
||||||
|
val name = doc.select("strong").text()
|
||||||
|
list.add(Vod(id, name, "", ""))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return Result.string(list)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,258 @@
|
||||||
|
package com.github.catvod.spider;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import com.github.catvod.bean.Result;
|
||||||
|
import com.github.catvod.bean.Vod;
|
||||||
|
import com.github.catvod.net.OkHttp;
|
||||||
|
import com.github.catvod.net.OkResult;
|
||||||
|
import com.github.catvod.utils.Json;
|
||||||
|
import com.github.catvod.utils.Util;
|
||||||
|
import com.google.gson.JsonArray;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class TgSearch123 extends Cloud {
|
||||||
|
private static final String KEY_API_URLS = "api_urls";
|
||||||
|
private static final String KEY_DOMAIN_MAP = "siteurl";
|
||||||
|
private static final String KEY_SOURCES = "sources";
|
||||||
|
private static final int DEFAULT_PAGE_SIZE = 10;
|
||||||
|
|
||||||
|
private List<String> apiUrls = new ArrayList<>();
|
||||||
|
private Map<String, String> domainMap = new HashMap<String, String>() {{
|
||||||
|
put("alipan", "阿里");
|
||||||
|
put("aliyundrive", "阿里");
|
||||||
|
put("quark", "夸克");
|
||||||
|
put("115cdn", "115");
|
||||||
|
put("baidu.com", "百度");
|
||||||
|
put("uc", "UC");
|
||||||
|
put("189.cn", "天翼");
|
||||||
|
put("139.com", "移动");
|
||||||
|
put("123", "123盘");
|
||||||
|
}};
|
||||||
|
private Set<String> sources = new HashSet<>();
|
||||||
|
private String[] extInfos = null;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void init(Context context, String extend) throws Exception {
|
||||||
|
super.init(context, extend);
|
||||||
|
|
||||||
|
this.apiUrls.clear();
|
||||||
|
|
||||||
|
if (!TextUtils.isEmpty(extend)) {
|
||||||
|
try {
|
||||||
|
if (extend.contains("###")) {
|
||||||
|
String[] infos = extend.split("###");
|
||||||
|
this.extInfos = infos;
|
||||||
|
|
||||||
|
if (infos.length > 0 && !TextUtils.isEmpty(infos[0])) {
|
||||||
|
processExtendConfig(infos[0]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.extInfos = new String[]{extend};
|
||||||
|
processExtendConfig(extend);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.extInfos = null; // 重置,避免内存泄漏
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processExtendConfig(String config) {
|
||||||
|
try {
|
||||||
|
// 检查是否为HTTP URL
|
||||||
|
if (isValidUrl(config)) {
|
||||||
|
if (!this.apiUrls.contains(config)) {
|
||||||
|
this.apiUrls.add(config);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 尝试JSON格式解析
|
||||||
|
JsonObject ext = Json.safeObject(config);
|
||||||
|
|
||||||
|
// 处理API URLs配置
|
||||||
|
processApiUrls(ext);
|
||||||
|
|
||||||
|
// 处理域名映射配置
|
||||||
|
processDomainMap(ext);
|
||||||
|
|
||||||
|
// 处理来源过滤配置
|
||||||
|
processSources(ext, true);
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 可以添加日志记录
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processApiUrls(JsonObject config) {
|
||||||
|
if (config.has(KEY_API_URLS) && config.get(KEY_API_URLS).isJsonArray()) {
|
||||||
|
JsonArray urlsArray = config.getAsJsonArray(KEY_API_URLS);
|
||||||
|
for (JsonElement element : urlsArray) {
|
||||||
|
if (element != null && element.isJsonPrimitive() && element.getAsJsonPrimitive().isString()) {
|
||||||
|
String url = element.getAsString();
|
||||||
|
if (isValidUrl(url) && !this.apiUrls.contains(url)) {
|
||||||
|
this.apiUrls.add(url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processDomainMap(JsonObject config) {
|
||||||
|
if (config.has(KEY_DOMAIN_MAP) && config.get(KEY_DOMAIN_MAP).isJsonObject()) {
|
||||||
|
JsonObject customDomains = config.getAsJsonObject(KEY_DOMAIN_MAP);
|
||||||
|
for (Map.Entry<String, JsonElement> entry : customDomains.entrySet()) {
|
||||||
|
if (entry == null || entry.getValue() == null) continue;
|
||||||
|
|
||||||
|
String domain = entry.getKey();
|
||||||
|
String sourceName = "";
|
||||||
|
try {
|
||||||
|
sourceName = entry.getValue().getAsString();
|
||||||
|
} catch (Exception e) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!TextUtils.isEmpty(domain) && !TextUtils.isEmpty(sourceName) && !domainMap.containsKey(domain)) {
|
||||||
|
domainMap.put(domain, sourceName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processSources(JsonObject config, boolean clearExisting) {
|
||||||
|
if (config.has(KEY_SOURCES) && config.get(KEY_SOURCES).isJsonArray()) {
|
||||||
|
if (clearExisting) {
|
||||||
|
this.sources.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonArray sourcesArray = config.getAsJsonArray(KEY_SOURCES);
|
||||||
|
for (JsonElement element : sourcesArray) {
|
||||||
|
if (element != null && element.isJsonPrimitive() && element.getAsJsonPrimitive().isString()) {
|
||||||
|
String source = element.getAsString();
|
||||||
|
if (!TextUtils.isEmpty(source) && !sources.contains(source)) {
|
||||||
|
sources.add(source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isValidUrl(String url) {
|
||||||
|
return !TextUtils.isEmpty(url) && (url.startsWith("http://") || url.startsWith("https://"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private Map<String, String> getHeader() {
|
||||||
|
Map<String, String> header = new HashMap<>();
|
||||||
|
header.put("User-Agent", Util.CHROME);
|
||||||
|
return header;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String searchContent(String key, boolean quick) throws Exception {
|
||||||
|
if (key == null || key.trim().isEmpty()) {
|
||||||
|
return Result.error("关键词不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
return performSearch(key, 1, DEFAULT_PAGE_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String performSearch(String key, int page, int pageSize) throws Exception {
|
||||||
|
List<Vod> list = new ArrayList<>();
|
||||||
|
int total = 0;
|
||||||
|
|
||||||
|
Map<String, String> params = new HashMap<>();
|
||||||
|
params.put("kw", key);
|
||||||
|
params.put("page", String.valueOf(page));
|
||||||
|
params.put("size", String.valueOf(pageSize));
|
||||||
|
|
||||||
|
for (String apiUrl : apiUrls) {
|
||||||
|
if (!isValidUrl(apiUrl)) continue;
|
||||||
|
|
||||||
|
try {
|
||||||
|
OkResult result = OkHttp.get(apiUrl, params, getHeader());
|
||||||
|
if (result.getCode() == 500 || TextUtils.isEmpty(result.getBody())) continue;
|
||||||
|
|
||||||
|
JsonObject jsonObject = Json.safeObject(result.getBody());
|
||||||
|
if (!jsonObject.has("code") || jsonObject.get("code").getAsInt() != 0 || !jsonObject.has("data"))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
JsonObject data = jsonObject.getAsJsonObject("data");
|
||||||
|
|
||||||
|
total = data.has("total") && !data.get("total").isJsonNull() ? data.get("total").getAsInt() : 0;
|
||||||
|
|
||||||
|
// 直接检查merged_by_type字段,无需三重判断
|
||||||
|
if (!data.has("merged_by_type") || !data.get("merged_by_type").isJsonObject()) continue;
|
||||||
|
|
||||||
|
JsonObject mergedByType = data.getAsJsonObject("merged_by_type");
|
||||||
|
|
||||||
|
for (Map.Entry<String, JsonElement> categoryEntry : mergedByType.entrySet()) {
|
||||||
|
if (!categoryEntry.getValue().isJsonArray()) continue;
|
||||||
|
|
||||||
|
JsonArray items = categoryEntry.getValue().getAsJsonArray();
|
||||||
|
|
||||||
|
for (JsonElement item : items) {
|
||||||
|
if (!item.isJsonObject()) continue;
|
||||||
|
|
||||||
|
JsonObject entry = item.getAsJsonObject();
|
||||||
|
|
||||||
|
String vodUrl = entry.has("url") && !entry.get("url").isJsonNull() ? entry.get("url").getAsString() : "";
|
||||||
|
if (TextUtils.isEmpty(vodUrl)) continue;
|
||||||
|
|
||||||
|
// 获取来源信息并映射
|
||||||
|
String originalSource = entry.has("source") && !entry.get("source").isJsonNull() ? entry.get("source").getAsString() : "未知来源";
|
||||||
|
String sourceName = mapSource(vodUrl, originalSource);
|
||||||
|
|
||||||
|
// 获取标题
|
||||||
|
String title = entry.has("note") && !entry.get("note").isJsonNull() ? entry.get("note").getAsString() : "未命名资源";
|
||||||
|
|
||||||
|
// 获取图片
|
||||||
|
String pic = getFirstImage(entry);
|
||||||
|
|
||||||
|
// 简化VodPlayBuilder的使用
|
||||||
|
Vod vod = new Vod(vodUrl, title, pic, sourceName + " (" + originalSource + ")");
|
||||||
|
// 由于只有一个播放源,直接设置播放信息
|
||||||
|
vod.setVodPlayFrom(sourceName);
|
||||||
|
vod.setVodPlayUrl("播放源$" + vodUrl);
|
||||||
|
|
||||||
|
// 来源过滤
|
||||||
|
if (sources.isEmpty() || sources.contains(sourceName)) {
|
||||||
|
list.add(vod);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int pageCount = total > 0 && pageSize > 0 ? (total + pageSize - 1) / pageSize : 1;
|
||||||
|
return Result.string(page, pageCount, pageSize, total, list);
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 出错时继续尝试下一个API
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.error("无法连接到任何搜索API");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提取来源映射逻辑为单独方法
|
||||||
|
private String mapSource(String vodUrl, String originalSource) {
|
||||||
|
for (Map.Entry<String, String> domainEntry : domainMap.entrySet()) {
|
||||||
|
if (vodUrl.contains(domainEntry.getKey())) {
|
||||||
|
return domainEntry.getValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return originalSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提取图片获取逻辑为单独方法
|
||||||
|
private String getFirstImage(JsonObject entry) {
|
||||||
|
if (entry.has("images") && !entry.get("images").isJsonNull() && entry.get("images").isJsonArray()) {
|
||||||
|
JsonArray images = entry.getAsJsonArray("images");
|
||||||
|
if (images.size() > 0 && !images.get(0).isJsonNull() && images.get(0).isJsonPrimitive()) {
|
||||||
|
return images.get(0).getAsString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue