Compare commits

...

300 Commits

Author SHA1 Message Date
github-actions 3b83ebef35 update spider jar and json 2025-12-15 06:40:41 +00:00
lushunming 9d67a07a84 uc刷新access token 2025-12-15 14:37:45 +08:00
github-actions 12ac9fd628 update spider jar and json 2025-12-15 05:32:35 +00:00
lushunming 84a3f4399b uc刷新access token 2025-12-15 13:29:55 +08:00
github-actions e467112e39 update spider jar and json 2025-12-15 03:26:32 +00:00
lushunming 6b0b4b6a74 uc刷新access token 2025-12-15 11:23:48 +08:00
github-actions 6c0841e4c5 update spider jar and json 2025-12-13 01:30:04 +00:00
lushunming ffffe95c47 Merge remote-tracking branch 'origin/multiThread' into multiThread 2025-12-13 09:11:32 +08:00
lushunming d1bb29a314 cloud异步 2025-12-13 09:11:08 +08:00
github-actions 858579c462 update spider jar and json 2025-12-13 00:23:15 +00:00
lushunming d280d770bb cloud异步 2025-12-13 08:09:34 +08:00
github-actions d488a00eab update spider jar and json 2025-12-12 08:36:45 +00:00
lushunming 2e028809e8 Merge remote-tracking branch 'origin/multiThread' into multiThread 2025-12-12 16:34:13 +08:00
lushunming 3002b235f9 cloud异步 2025-12-12 16:33:28 +08:00
github-actions c28917b071 update spider jar and json 2025-12-12 08:27:15 +00:00
lushunming 61dcb1d4f3 cloud异步 2025-12-12 16:24:41 +08:00
github-actions 1c3929d920 update spider jar and json 2025-12-12 08:18:19 +00:00
lushunming 4b66ac8c19 Merge remote-tracking branch 'origin/multiThread' into multiThread 2025-12-12 16:15:48 +08:00
lushunming 68b72532e8 cloud异步 2025-12-12 16:15:37 +08:00
github-actions 58ccf67b2d update spider jar and json 2025-12-09 07:45:52 +00:00
lushunming cb27d8b6d0 Merge remote-tracking branch 'origin/multiThread' into multiThread
# Conflicts:
#	json/index.json
2025-12-09 15:43:03 +08:00
lushunming ed7963420f action 2025-12-09 15:42:50 +08:00
github-actions 6e56986b19 update spider jar and json 2025-12-09 07:30:22 +00:00
lushunming 661727b7f0 Merge remote-tracking branch 'origin/multiThread' into multiThread 2025-12-09 15:27:48 +08:00
lushunming eacd5fff22 action 2025-12-09 15:27:38 +08:00
github-actions 4a57536a3a update spider jar and json 2025-12-09 07:25:38 +00:00
lushunming d31a069b85 action 2025-12-09 15:22:57 +08:00
github-actions d0f6e7b601 update spider jar and json 2025-12-09 07:09:59 +00:00
github-actions f90f88cfc5 update spider jar and json 2025-12-09 07:01:46 +00:00
github-actions 1ed471cb16 update spider jar and json 2025-12-09 06:48:25 +00:00
lushunming 514d49f2f5 Merge remote-tracking branch 'origin/multiThread' into multiThread 2025-12-09 14:45:54 +08:00
lushunming 5dd19bc9e9 action 2025-12-09 14:45:44 +08:00
github-actions 77196c2037 update spider jar and json 2025-12-09 06:35:04 +00:00
lushunming 4f56a6f6d7 Merge remote-tracking branch 'origin/multiThread' into multiThread 2025-12-09 14:32:12 +08:00
lushunming 0a90b60ce1 action 2025-12-09 14:32:01 +08:00
github-actions f80c8ab7d1 update spider jar and json 2025-12-09 06:29:29 +00:00
lushunming f0624a9014 Merge remote-tracking branch 'origin/multiThread' into multiThread 2025-12-09 14:26:48 +08:00
lushunming 5130a64ba6 action 2025-12-09 14:26:35 +08:00
github-actions 235dfd716d update spider jar and json 2025-12-09 06:23:16 +00:00
lushunming cf427b4224 Merge remote-tracking branch 'origin/multiThread' into multiThread 2025-12-09 14:20:41 +08:00
lushunming e5833f43a9 action 2025-12-09 14:20:31 +08:00
github-actions 8e0d555073 update spider jar and json 2025-12-09 06:19:02 +00:00
lushunming c5e74b576a Merge remote-tracking branch 'origin/multiThread' into multiThread 2025-12-09 14:16:10 +08:00
lushunming d671e7090b action 2025-12-09 14:14:31 +08:00
github-actions 2b0bdcf9ce update spider jar and json 2025-12-09 06:01:04 +00:00
lushunming c40988d997 action 2025-12-09 13:56:38 +08:00
lushunming 976fc63f4d action 2025-12-09 13:50:41 +08:00
github-actions ebbe5360c3 update spider jar and json 2025-12-09 05:48:02 +00:00
lushunming 354d818fbf action 2025-12-09 13:44:40 +08:00
github-actions 00db8e8173 update spider jar and json 2025-12-09 05:31:10 +00:00
lushunming 4312efd414
Update genJar.yml 2025-12-09 13:28:36 +08:00
github-actions 59b48044b7 update spider jar and json 2025-12-09 03:52:15 +00:00
lushunming da79d74b06
Update genJar.yml 2025-12-09 11:49:46 +08:00
github-actions 271c56767c update spider jar and json 2025-12-09 03:48:12 +00:00
lushunming 96272da9fc Merge remote-tracking branch 'origin/multiThread' into multiThread
# Conflicts:
#	json/index.json
2025-12-09 11:45:19 +08:00
lushunming 2e95dbcc9c quark根据文件名判断是否是视频 2025-12-09 11:44:55 +08:00
github-actions 6ec990b2f1 update spider jar and json 2025-12-08 03:03:29 +00:00
lushunming 537a84cc5b quark根据文件名判断是否是视频 2025-12-08 11:00:47 +08:00
github-actions bbb4d37922 update spider jar and json 2025-12-08 02:05:59 +00:00
lushunming b753ae7fa4 魔数前增加恶意头检测 2025-12-08 10:03:25 +08:00
lushunming 4204acc672 Merge remote-tracking branch 'origin/multiThread' into multiThread 2025-12-08 09:59:00 +08:00
lushunming 3540ecc3aa 魔数前增加恶意头检测 2025-12-08 09:58:45 +08:00
github-actions af044f37c4 update spider jar and json 2025-12-05 02:48:59 +00:00
lushunming ed88ad03cf 百度保证每次使用只会清理一次文件夹 2025-12-05 10:46:20 +08:00
github-actions 2b88bf1c9d update spider jar and json 2025-12-01 02:27:05 +00:00
lushunming b96fd2e894 Merge remote-tracking branch 'origin/multiThread' into multiThread 2025-12-01 10:24:35 +08:00
lushunming 2d0ad541fc uc和quark 优化 2025-12-01 09:57:30 +08:00
github-actions 9096420bac update spider jar and json 2025-11-27 03:32:14 +00:00
lushunming f107976989 Merge remote-tracking branch 'origin/multiThread' into multiThread 2025-11-27 11:29:50 +08:00
lushunming 089113fbe4 uc和quark空指针 2025-11-27 11:29:35 +08:00
github-actions 7e26202bec update spider jar and json 2025-11-27 03:23:37 +00:00
lushunming 8a8991f03f Merge remote-tracking branch 'origin/multiThread' into multiThread 2025-11-27 11:21:07 +08:00
lushunming a0bd2a128a uc和quark空指针 2025-11-27 11:16:17 +08:00
github-actions 394601dc07 update spider jar and json 2025-11-27 02:49:00 +00:00
lushunming 69ae330c7a uc和quark空指针 2025-11-27 10:46:06 +08:00
github-actions 608e1b51a1 update spider jar and json 2025-11-25 03:00:57 +00:00
lushunming 58e158be75 Merge remote-tracking branch 'origin/multiThread' into multiThread 2025-11-25 10:58:25 +08:00
lushunming bb5c605425 格式美化 2025-11-25 10:58:16 +08:00
github-actions cff732fdac update spider jar and json 2025-11-25 01:11:11 +00:00
lushunming 448f2cf2c3 SeedHub 2025-11-25 09:08:19 +08:00
github-actions 2886403c73 update spider jar and json 2025-11-25 00:50:55 +00:00
lushunming 104a9c9b8a Merge remote-tracking branch 'origin/multiThread' into multiThread 2025-11-25 08:48:19 +08:00
lushunming 36bcf9fbf1 SeedHub 2025-11-25 08:48:08 +08:00
github-actions 20d7bed2f9 update spider jar and json 2025-11-24 00:30:08 +00:00
lushunming b5e302aa99 Merge remote-tracking branch 'origin/multiThread' into multiThread 2025-11-24 08:27:36 +08:00
lushunming bc672c7965 分块调节 2025-11-24 08:26:41 +08:00
github-actions 39248d9a0a update spider jar and json 2025-11-18 07:39:40 +00:00
lushunming 08edebc431 Merge remote-tracking branch 'origin/multiThread' into multiThread 2025-11-18 15:37:03 +08:00
lushunming 12b675da04 代理视频的异常捕获 2025-11-18 14:42:01 +08:00
github-actions 93a5981c39 update spider jar and json 2025-11-17 00:18:49 +00:00
lushunming 240e9fb3b0 Merge remote-tracking branch 'origin/multiThread' into multiThread 2025-11-17 08:16:03 +08:00
lushunming 26d525f436 代理视频的异常捕获 2025-11-17 08:15:51 +08:00
github-actions 67a6cc2aa6 update spider jar and json 2025-11-15 01:29:07 +00:00
lushunming 565c92ce63 Merge remote-tracking branch 'origin/multiThread' into multiThread 2025-11-15 09:23:39 +08:00
lushunming 7f49ff239f tg搜 2025-11-15 09:19:09 +08:00
github-actions 0cdff801a4 update spider jar and json 2025-11-06 23:30:44 +00:00
lushunming a3386d343a Merge remote-tracking branch 'origin/multiThread' into multiThread 2025-11-07 07:27:55 +08:00
lushunming 52347dd0fd 网盘优化 2025-11-07 07:24:26 +08:00
github-actions 3043f08f4c update spider jar and json 2025-11-05 13:27:17 +00:00
lushunming ecd1467d33 Merge remote-tracking branch 'origin/multiThread' into multiThread
# Conflicts:
#	jar/custom_spider.jar
#	jar/custom_spider.jar.md5
#	json/index.json
2025-11-05 21:24:20 +08:00
lushunming 28a473a084 删除代码 2025-11-05 16:06:10 +08:00
github-actions cf45ab210c update spider jar and json 2025-11-03 08:10:09 +00:00
lushunming 4fb678c4bf Merge remote-tracking branch 'origin/multiThread' into multiThread 2025-11-03 16:07:46 +08:00
lushunming 85bff735dd 优化下载 2025-11-03 16:07:31 +08:00
github-actions 12832816fe update spider jar and json 2025-11-03 08:04:30 +00:00
lushunming d6ee012dff Merge remote-tracking branch 'origin/multiThread' into multiThread 2025-11-03 16:01:51 +08:00
lushunming 1e7e846c5b 优化下载 2025-11-03 16:01:40 +08:00
github-actions 45b5dcdfba update spider jar and json 2025-11-03 07:22:03 +00:00
lushunming bc388773d0 Merge remote-tracking branch 'origin/multiThread' into multiThread 2025-11-03 15:19:41 +08:00
lushunming 03276cf9dd 构建问题 2025-11-03 15:14:15 +08:00
github-actions 537583e59b update spider jar and json 2025-11-03 07:08:09 +00:00
lushunming 9c00eb51d5 构建问题 2025-11-03 15:05:37 +08:00
lushunming 683621c729 构建问题 2025-11-03 14:56:03 +08:00
lushunming 5c4b3c3ab4 构建问题 2025-11-03 14:48:49 +08:00
lushunming 4e7ba34997 构建问题 2025-11-03 14:24:40 +08:00
lushunming ab6b0f030c 构建问题 2025-11-03 14:23:31 +08:00
lushunming 7aaec80904 构建问题 2025-11-03 14:21:00 +08:00
lushunming 986f2009f7 构建问题 2025-11-03 14:20:13 +08:00
lushunming 8ac4501b1d 构建问题 2025-11-03 14:18:42 +08:00
lushunming 49b2c7c1e5 构建问题 2025-11-03 14:10:20 +08:00
lushunming c96d3dcca1 天翼问题修复 2025-11-03 11:49:12 +08:00
lushunming b47e9b12ac 123问题修复 2025-10-30 16:08:43 +08:00
lushunming 2d4d690431 Qupans 2025-10-30 10:48:12 +08:00
lushunming aabb9d7698 Qupans 2025-10-24 14:02:58 +08:00
lushunming 001f56c842 百度bug 2025-10-21 16:20:57 +08:00
lushunming e38debf3c1 123盘expire 2025-10-21 13:46:29 +08:00
lushunming 586b4919c7 123盘expire 2025-10-21 11:50:17 +08:00
lushunming 35ab144e27 123盘基本完成 2025-10-21 10:52:14 +08:00
lushunming 0fd1d6df7c 123盘基本完成 2025-10-21 10:44:20 +08:00
lushunming fc1403c4a8 123盘测试 2025-10-21 10:37:58 +08:00
lushunming debbcad7be 123盘测试 2025-10-21 10:28:57 +08:00
lushunming 3449968aa8 123盘测试 2025-10-21 10:18:29 +08:00
lushunming 4e873ca2a9 123盘测试 2025-10-21 10:11:40 +08:00
lushunming 09324a563d 123盘测试 2025-10-21 09:53:59 +08:00
lushunming de261ec674 123盘测试 2025-10-21 09:48:36 +08:00
lushunming 62e00061fd 123盘测试 2025-10-21 09:29:57 +08:00
lushunming 58b9479dc4 123盘测试 2025-10-21 09:22:44 +08:00
lushunming 9f224ab326 123盘测试 2025-10-21 08:55:41 +08:00
lushunming 4f31d98da5 123盘测试 2025-10-21 08:34:32 +08:00
lushunming d15d2874e1 123盘测试 2025-10-21 07:44:06 +08:00
lushunming 445810ddc0 123盘测试 2025-10-20 15:45:55 +08:00
lushunming 1e2766edcb 123盘测试 2025-10-20 15:30:09 +08:00
lushunming c1443930da 123盘测试 2025-10-20 15:13:45 +08:00
lushunming 228cd96bf3 123盘测试 2025-10-20 14:34:55 +08:00
lushunming f9fee85737 123盘测试 2025-10-20 14:25:56 +08:00
lushunming 528376b09e 123盘测试 2025-10-20 14:15:05 +08:00
lushunming 96f1dc0757 123盘测试 2025-10-20 14:08:10 +08:00
lushunming 5387abbd61 123盘测试 2025-10-20 13:38:55 +08:00
lushunming 05685aab0b 123盘测试 2025-10-20 11:55:45 +08:00
lushunming a87f9ca53d 123盘测试 2025-10-20 11:43:08 +08:00
lushunming 6de76787e6 123盘测试 2025-10-20 11:04:20 +08:00
lushunming d847d8ff49 123盘测试 2025-10-20 10:41:03 +08:00
lushunming 3ffe3e459d 123盘测试 2025-10-20 10:19:56 +08:00
lushunming 80fc42168f 123盘测试 2025-10-20 09:11:51 +08:00
lushunming f706fa34af 123盘测试 2025-10-20 09:09:08 +08:00
lushunming 2feb20da84 123盘测试 2025-10-20 08:02:27 +08:00
lushunming f7b13b1b21 百度扫码登录优化 2025-10-17 13:24:55 +08:00
lushunming 932e6ebab5 百度扫码登录优化 2025-10-17 11:52:46 +08:00
lushunming bb57f23567 百度扫码登录 2025-10-17 11:03:25 +08:00
lushunming 69555118cb 百度扫码登录 2025-10-17 10:51:19 +08:00
lushunming e67b2d874a 百度扫码登录 2025-10-17 10:20:18 +08:00
lushunming 9afcc6e32a Rename .java to .kt 2025-10-17 10:20:16 +08:00
lushunming 1e139be660 百度扫码登录 2025-10-17 10:00:23 +08:00
lushunming 08b5acbf0a TGsou百度 2025-10-15 11:35:50 +08:00
lushunming 33a100d5e6 TGsou百度 2025-10-15 11:27:13 +08:00
lushunming ccf4c0b068 TGsou百度 2025-10-15 08:53:26 +08:00
lushunming c9ebf3c7d7 bd网盘完成 2025-10-14 17:06:24 +08:00
lushunming 873e10b31e bd网盘 2025-10-14 17:00:57 +08:00
lushunming b672af3b1d bd网盘 2025-10-14 16:53:50 +08:00
lushunming dfe4a8f626 bd网盘 2025-10-14 16:39:36 +08:00
lushunming 1b1cb8e7a0 bd网盘 2025-10-14 16:21:07 +08:00
lushunming e00bb67f74 bd网盘 2025-10-14 16:05:47 +08:00
lushunming 57abcc97b6 bd网盘 2025-10-14 15:51:35 +08:00
lushunming f8101addf2 bd网盘 2025-10-14 15:50:05 +08:00
lushunming eeac79f772 bd网盘 2025-10-13 16:23:20 +08:00
lushunming 45f3cfa1ab DiDa写了一半,不高兴搞了 2025-10-10 15:07:57 +08:00
lushunming e924e4f53b 热播影视 2025-10-09 14:51:43 +08:00
lushunming d54be2ad8e 合并流返回代码 2025-09-23 15:53:55 +08:00
lushunming a578aaf6c3 合并流返回代码 2025-07-23 13:34:44 +08:00
lushunming 112eedbe36 修改 2025-07-23 09:28:23 +08:00
lushunming 23ca8d26f1 修改 2025-07-23 09:23:58 +08:00
lushunming 10c92b748c 修改 2025-07-23 08:43:55 +08:00
lushunming dc70fd77d5 修改 2025-07-23 08:43:49 +08:00
lushunming 4cb62ebba6 修改 2025-07-22 17:11:16 +08:00
lushunming 16fb9d182e 修改 2025-07-22 16:42:35 +08:00
lushunming d3ac6bd080 json问题 2025-07-22 16:18:17 +08:00
lushunming 01b7fbb09d httpserver 2025-07-22 14:27:12 +08:00
lushunming f5cce91a9c httpserver 2025-07-22 14:17:45 +08:00
lushunming a7485e82ae httpserver 2025-07-22 14:06:44 +08:00
lushunming e18a721e44 httpserver 2025-07-22 13:29:39 +08:00
lushunming 226f41d386 httpserver 2025-07-22 11:48:02 +08:00
lushunming 9e61895f05 httpserver 2025-07-22 11:37:13 +08:00
lushunming a328a51b03 httpserver 2025-07-22 10:41:07 +08:00
lushunming 397b67a704 httpserver 2025-07-22 10:11:27 +08:00
lushunming 9fcf1455e8 httpserver 2025-07-22 09:59:26 +08:00
lushunming 2d2d3377c4 httpserver 2025-07-21 15:35:12 +08:00
lushunming 8e35b95ac9 httpserver 2025-07-21 15:29:33 +08:00
lushunming b5a1f6ef04 httpserver 2025-07-21 15:24:56 +08:00
lushunming 7e65d5eae0 httpserver 2025-07-21 15:11:47 +08:00
lushunming 9e6b74e8ee httpserver 2025-07-21 15:02:08 +08:00
lushunming 48a6d353b6 httpserver 2025-07-21 14:56:16 +08:00
lushunming fd136460b4 httpserver 2025-07-21 14:51:23 +08:00
lushunming 290163871d httpserver 2025-07-21 14:14:55 +08:00
lushunming 3bad2e95d2 httpserver 2025-07-21 14:07:37 +08:00
lushunming 41f91e1de2 httpserver 2025-07-21 14:04:44 +08:00
lushunming 8769c591bb httpserver 2025-07-21 13:50:30 +08:00
lushunming 6b2949e834 httpserver 2025-07-21 11:50:08 +08:00
lushunming ce3e5eb69e httpserver 2025-07-21 11:37:20 +08:00
lushunming 078f8a701e httpserver 2025-07-21 10:58:51 +08:00
“lushunming” a1f8601481 kotlin 修改 2025-07-20 17:45:31 +08:00
lushunming c523058923 httpserver 2025-07-19 20:43:34 +08:00
lushunming 55463407e0 httpserver 2025-07-19 20:43:09 +08:00
“lushunming” 882b07dd41 kotlin 修改 2025-07-18 22:16:26 +08:00
lushunming 787b35c979 ktor 2025-07-18 15:32:00 +08:00
lushunming 94b5acd6db ktor 2025-07-18 15:19:04 +08:00
lushunming e970820b9b ktor 2025-07-18 15:10:59 +08:00
lushunming 48f25a825b ktor 2025-07-18 14:32:12 +08:00
lushunming 9f89678c45 ktor 2025-07-18 14:20:31 +08:00
lushunming 032e496a26 ktor 2025-07-18 14:03:12 +08:00
lushunming 630a4552d6 ktor 2025-07-18 13:36:47 +08:00
lushunming 6c98203b45 ktor 2025-07-18 13:33:14 +08:00
lushunming 382c82513f ktor 2025-07-18 12:01:08 +08:00
lushunming 3ff21b70ed ktor 2025-07-18 11:27:52 +08:00
lushunming 925fa80473 ktor 2025-07-18 11:13:49 +08:00
lushunming db0752a530 ktor 2025-07-18 10:57:31 +08:00
lushunming de412d36d1 ktor 2025-07-18 10:51:37 +08:00
lushunming 7763f6646c ktor 2025-07-18 10:38:57 +08:00
lushunming a93236ade6 ktor 2025-07-18 10:26:48 +08:00
lushunming ada7f1d27d ktor 2025-07-18 10:14:55 +08:00
lushunming 0eef0a12f3 ktor 2025-07-18 09:57:23 +08:00
lushunming 31be601d72 ktor 2025-07-18 09:23:41 +08:00
lushunming 2db0d6b20d ktor 2025-07-18 07:21:03 +08:00
lushunming 1f404c5306 ktor 2025-07-18 06:50:01 +08:00
lushunming 605a501496 分片数优化 2025-07-15 15:54:52 +08:00
lushunming 17b714cfe7 分片数优化 2025-07-15 15:47:48 +08:00
lushunming 2aba6bfba5 分片数优化 2025-07-15 15:40:17 +08:00
lushunming a9f3aa3f74 分片数优化 2025-07-15 15:35:49 +08:00
lushunming 01e9b00a36 分片数优化 2025-07-15 15:24:39 +08:00
lushunming 8a5d18dbb1 分片数优化 2025-07-15 15:16:22 +08:00
lushunming 976a5b441f 分片数优化 2025-07-15 14:57:41 +08:00
lushunming bbf477acb5 分片数优化 2025-07-15 14:33:32 +08:00
lushunming 6301abfe6a tg搜 2025-07-14 15:42:16 +08:00
lushunming f56e03cf0b tg搜 2025-07-14 15:35:51 +08:00
lushunming f6c9223f65 Rename .java to .kt 2025-07-14 15:35:49 +08:00
lushunming 09a8fc5a4f kt线程和分片优化 2025-07-14 14:01:05 +08:00
lushunming 8e5a2be92a Merge branch 'multiThreadkt' into multiThread 2025-07-14 13:58:03 +08:00
lushunming 71cf47a76e kt线程和分片优化 2025-07-14 13:52:00 +08:00
lushunming 295bc67569 kt线程和分片优化 2025-07-14 13:49:38 +08:00
lushunming 4f47bd68c6 kt线程和分片优化 2025-07-14 10:27:38 +08:00
lushunming 946ff4a6f9 kt线程和分片优化 2025-07-14 08:35:50 +08:00
lushunming aa41a5fed1 kt线程和分片优化 2025-07-14 08:35:21 +08:00
lushunming f4facd0f69 kt线程和分片优化 2025-07-14 08:31:04 +08:00
lushunming da0834e46e kt线程和分片优化 2025-07-14 08:21:18 +08:00
lushunming da70913280 kt线程和分片优化 2025-07-14 08:05:14 +08:00
lushunming c7752dc1be kt线程和分片优化 2025-07-14 07:44:28 +08:00
“lushunming” 22b771615f Merge remote-tracking branch 'origin/multiThread' into multiThread
# Conflicts:
#	jar/custom_spider.jar
#	jar/custom_spider.jar.md5
2025-07-13 20:47:20 +08:00
“lushunming” 0bd03f5140 kotlin 修改 2025-07-13 20:46:57 +08:00
“lushunming” 977e98c2b4 kotlin 修改 2025-07-13 11:03:57 +08:00
lushunming 1bf138ada9 kt携程版本 2025-07-12 10:07:49 +08:00
lushunming bcaa6f18bd kt携程版本 2025-07-12 10:01:17 +08:00
lushunming 3b5d167662 kt携程版本 2025-07-12 08:57:02 +08:00
lushunming a311812cf3 kt携程版本 2025-07-12 08:43:44 +08:00
lushunming eeaf8f620f kt携程版本 2025-07-12 08:39:15 +08:00
lushunming 8a169467c0 kt携程版本 2025-07-12 06:59:26 +08:00
lushunming ad148795a8 kt携程版本 2025-07-11 11:48:06 +08:00
lushunming 191d961f84 kt携程版本 2025-07-11 11:33:10 +08:00
lushunming d64fdb3416 夸克清理磁盘 2025-07-11 08:51:11 +08:00
lushunming 2a0682ea22 content-type 有时为空 2025-07-10 13:56:12 +08:00
lushunming 90d4f7528b content-type 有时为空 2025-07-10 13:54:17 +08:00
lushunming cfadc7403a content-type 有时为空 2025-07-10 11:40:41 +08:00
lushunming 27c57401e9 多线程优化,太大的视频缓存10M,小视频2M 2025-07-08 14:13:27 +08:00
lushunming 3662843f46 139原画多线程优化 2025-07-08 13:37:32 +08:00
lushunming e29f61d9b6 139原画多线程 2025-07-08 11:57:17 +08:00
lushunming 01afad7c4f 139原画多线程 2025-07-08 11:49:40 +08:00
lushunming a294e5cc7b 139原画多线程 2025-07-08 11:45:03 +08:00
lushunming 82e60f9db7 Merge branch '139login' into multiThread
# Conflicts:
#	jar/custom_spider.jar
#	jar/custom_spider.jar.md5
#	json/test.json
2025-07-08 11:32:22 +08:00
lushunming 438f0325f6 139login 2025-07-08 11:01:30 +08:00
lushunming 72a2db4f3a 多线程加速优化 2025-07-07 13:38:09 +08:00
lushunming 85bf3bc33a 多线程加速优化 2025-07-07 11:52:45 +08:00
lushunming 92f7417ffe 多线程加速优化 2025-07-07 11:48:28 +08:00
lushunming 619403dbc6 多线程加速优化 2025-07-07 11:35:57 +08:00
“lushunming” 272d9db20c 多线程内存分割 2025-07-06 14:42:35 +08:00
lushunming e41998b0c5 多线程测试 2025-06-24 14:15:39 +08:00
lushunming 989157a852 多线程测试 2025-06-24 14:12:01 +08:00
lushunming 5b6e3ec8a8 多线程测试 2025-06-19 14:52:39 +08:00
lushunming 086134a8ed 多线程测试 2025-06-19 14:24:45 +08:00
lushunming b59541f6e2 多线程测试 2025-06-18 16:27:57 +08:00
lushunming e3cde4f4a0 多线程测试 2025-06-18 16:15:13 +08:00
lushunming 33b5401337 多线程测试 2025-06-18 16:07:01 +08:00
lushunming 68daa42635 多线程测试 2025-06-18 15:55:44 +08:00
lushunming ca1754e3f0 多线程测试 2025-06-18 15:46:48 +08:00
lushunming ff2ecbeefc 多线程测试 2025-06-18 15:22:38 +08:00
lushunming 68c4b89344 多线程测试 2025-06-18 14:32:03 +08:00
lushunming 25cd597ae6 多线程测试 2025-06-17 17:20:45 +08:00
lushunming a11264eb07 多线程测试 2025-06-17 17:15:54 +08:00
lushunming 5be2bf0247 多线程测试 2025-06-17 17:06:00 +08:00
lushunming 6b20fa8ea5 多线程测试 2025-06-17 16:54:40 +08:00
lushunming 99aeeea4bd 多线程测试 2025-06-17 16:46:29 +08:00
lushunming 396379e571 多线程测试 2025-06-17 16:37:54 +08:00
lushunming 459038157d 多线程测试 2025-06-17 14:31:49 +08:00
87 changed files with 7693 additions and 9487 deletions

View File

@ -1,27 +1,80 @@
name: Spider Jar Gen CI name: Spider Jar Gen CI
on: workflow_dispatch on:
push:
branches: [ "multiThread" ]
workflow_dispatch:
jobs: jobs:
build: build:
runs-on: windows-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v4
- name: set up JDK 21 - name: Set up JDK 21
uses: actions/setup-java@v2 uses: actions/setup-java@v5
with: with:
java-version: '21' java-version: '21'
distribution: 'adopt' distribution: 'jetbrains'
cache: gradle cache: gradle
- name: Build with Gradle - name: Grant execute permission to gradlew
run: ./build.bat ec run: chmod +x ./gradlew
- name: Update spider jar - name: Build with Gradle
uses: EndBug/add-and-commit@v7 run: ./gradlew app:buildCustomSpiderJar
- name: Add & Commit
uses: EndBug/add-and-commit@v9.1.4
with: with:
default_author: github_actions default_author: github_actions
message: 'update spider jar' message: 'update spider jar and json'
add: "['./jar/custom_spider.jar', './jar/custom_spider.jar.md5']" 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

View File

@ -2,6 +2,7 @@ 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' id 'org.jetbrains.kotlin.android'
} }
java { java {
toolchain { toolchain {
@ -47,6 +48,12 @@ android {
jvmTarget = "11" jvmTarget = "11"
} }
packagingOptions {
exclude 'META-INF/*'
}
configurations.configureEach { configurations.configureEach {
resolutionStrategy { resolutionStrategy {
@ -78,9 +85,139 @@ dependencies {
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.1" 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 'wang.harlon.quickjs:wrapper-java:1.0.0'
// implementation(ext: 'aar', name: 'quickjs', group: 'fongmi', version: 'release') // implementation(ext: 'aar', name: 'quickjs', group: 'fongmi', version: 'release')
// api 'wang.harlon.quickjs:wrapper-android:2.0.0' // 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.

View File

@ -44,3 +44,12 @@
# Please add these rules to your existing keep rules in order to suppress warnings. # Please add these rules to your existing keep rules in order to suppress warnings.
# This is generated automatically by the Android Gradle plugin. # This is generated automatically by the Android Gradle plugin.
-dontwarn org.bouncycastle.jce.provider.BouncyCastleProvider -dontwarn org.bouncycastle.jce.provider.BouncyCastleProvider
-keepattributes SourceFile,LineNumberTable
# 禁用代码混淆
#-dontobfuscate

View File

@ -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) {

View File

@ -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)
}
}

View File

@ -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()
}
}

View File

@ -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();
}
}

View File

@ -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: $unamepasswd$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) {
}
}
}

View File

@ -12,7 +12,6 @@ import android.view.ViewGroup;
import android.widget.EditText; import android.widget.EditText;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import android.widget.ImageView; import android.widget.ImageView;
import com.github.catvod.bean.Result; import com.github.catvod.bean.Result;
import com.github.catvod.bean.Vod; import com.github.catvod.bean.Vod;
import com.github.catvod.bean.quark.Cache; import com.github.catvod.bean.quark.Cache;
@ -26,7 +25,6 @@ import com.github.catvod.spider.Init;
import com.github.catvod.spider.Proxy; import com.github.catvod.spider.Proxy;
import com.github.catvod.utils.*; import com.github.catvod.utils.*;
import com.google.gson.Gson; import com.google.gson.Gson;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
@ -73,7 +71,7 @@ public class QuarkApi {
if (Util.getExt(url).contains("m3u8")) { if (Util.getExt(url).contains("m3u8")) {
return getM3u8(url, header); return getM3u8(url, header);
} }
return ProxyVideo.proxy(url, header); return ProxyVideo.proxyMultiThread(url, header);
} }
/** /**
@ -212,17 +210,20 @@ public class QuarkApi {
String fileId = split[0], fileToken = split[1], shareId = split[2], stoken = split[3]; String fileId = split[0], fileToken = split[1], shareId = split[2], stoken = split[3];
String playUrl = ""; String playUrl = "";
if (flag.contains("quark原画")) {
playUrl = this.getDownload(shareId, stoken, fileId, fileToken, true);
} else {
playUrl = this.getLiveTranscoding(shareId, stoken, fileId, fileToken, flag);
}
Map<String, String> header = getHeaders(); Map<String, String> header = getHeaders();
header.remove("Host"); header.remove("Host");
header.remove("Content-Type"); 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(); return Result.get().url(proxyVideoUrl(playUrl, header)).octet().header(header).string();
} }
}
private String proxyVideoUrl(String url, Map<String, String> header) { 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()))); 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())));
} }
@ -377,7 +378,7 @@ public class QuarkApi {
public List<String> getPlayFormatList() { public List<String> getPlayFormatList() {
if (this.isVip) { if (this.isVip) {
return Arrays.asList("4K", "超清", "高清", "普画"); return Arrays.asList("4K"/*, "超清", "高清", "普画"*/);
} else { } else {
return Collections.singletonList("普画"); return Collections.singletonList("普画");
} }
@ -413,7 +414,7 @@ public class QuarkApi {
for (Map<String, Object> item : items) { for (Map<String, Object> item : items) {
if (Boolean.TRUE.equals(item.get("dir"))) { if (Boolean.TRUE.equals(item.get("dir"))) {
subDir.add(item); subDir.add(item);
} else if (Boolean.TRUE.equals(item.get("file")) && "video".equals(item.get("obj_category"))) { } else if (Boolean.TRUE.equals(item.get("file")) && (Util.isMedia((String) item.get("file_name")))) {
if ((Double) item.get("size") < 1024 * 1024 * 5) continue; if ((Double) item.get("size") < 1024 * 1024 * 5) continue;
item.put("stoken", this.shareTokenCache.get(shareData.getShareId()).get("stoken")); item.put("stoken", this.shareTokenCache.get(shareData.getShareId()).get("stoken"));
videos.add(Item.objectFrom(item, shareData.getShareId(), shareIndex)); videos.add(Item.objectFrom(item, shareData.getShareId(), shareIndex));
@ -474,14 +475,17 @@ public class QuarkApi {
} }
private void clearSaveDir() throws Exception { 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); 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<>(); if (listData.get("data") != null) {
for (Map<String, Object> stringStringMap : ((List<Map<String, Object>>) ((Map<String, Object>) listData.get("data")).get("list"))) { List<Map<String, Object>> fileList = (List<Map<String, Object>>) ((Map<String, Object>) listData.get("data")).get("list");
list.add((String) stringStringMap.get("fid")); 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");
} }
api("file/delete?" + this.pr, Collections.emptyMap(), Map.of("action_type", "2", "filelist", Json.toJson(list), "exclude_fids", ""), 0, "POST");
} }
} }

View File

@ -23,7 +23,7 @@ import java.util.regex.Pattern;
public class TianyiApi { public class TianyiApi {
private String apiUrl = "https://cloud.189.cn/api/open/share/"; private String apiUrl = "https://cloud.189.cn/api/open/share/";
public static final String URL_START = "https://cloud.189.cn/"; public static final String URL_CONTAIN = "cloud.189.cn/";
private Map<String, JsonObject> shareTokenCache = new HashMap<>(); private Map<String, JsonObject> shareTokenCache = new HashMap<>();
@ -175,7 +175,7 @@ public class TianyiApi {
header.put("Cookie", cookieJar.loadForRequest("https://cloud.189.cn/api/portal/getNewVlcVideoPlayUrl.action")); header.put("Cookie", cookieJar.loadForRequest("https://cloud.189.cn/api/portal/getNewVlcVideoPlayUrl.action"));
return Result.get().url(ProxyVideo.buildCommonProxyUrl(playUrl, header)).octet().header(header).string(); return Result.get().url(ProxyServer.INSTANCE.buildProxyUrl(playUrl, header)).octet().header(header).string();
} }
@ -221,41 +221,30 @@ public class TianyiApi {
public ShareData getShareData(String url, String accessCode) { public ShareData getShareData(String url, String accessCode) {
String shareCode = ""; // 从整个URL中直接提取访问码无论格式如何
// 第一种匹配规则使用预编译的 regex Matcher accessMatcher = Pattern.compile("访问码[:]([a-zA-Z0-9]+)").matcher(url);
Matcher matcher = Pattern.compile("https:\\/\\/cloud\\.189\\.cn\\/web\\/share\\?code=([^&]+)").matcher(url);
if (matcher.find() && matcher.group(1) != null) {
shareCode = matcher.group(1);
// 从shareCode中提取访问码
Matcher accessMatcher = Pattern.compile("访问码:([a-zA-Z0-9]+)").matcher(shareCode);
if (accessMatcher.find()) { if (accessMatcher.find()) {
accessCode = accessMatcher.group(1); accessCode = accessMatcher.group(1);
} else if (accessCode == null || accessCode.isEmpty()) {
} else {
accessCode = ""; 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 { } else {
// 第二种匹配规则直接匹配 cloud.189.cn/t/ 格式 // 第二种匹配规则直接匹配 cloud.189.cn/t/ 格式兼容http和https匹配到空格前
Matcher fallbackMatcher = Pattern.compile("https://cloud\\.189\\.cn/t/([^&]+)").matcher(url); Matcher fallbackMatcher = Pattern.compile("https?:\\/\\/cloud\\.189\\.cn\\/t/([^\\s]+)").matcher(url);
if (fallbackMatcher.find()) { if (fallbackMatcher.find()) {
shareCode = fallbackMatcher.group(1); shareCode = fallbackMatcher.group(1);
} else {
shareCode = null;
}
// 再次尝试从shareCode提取访问码
if (shareCode != null) {
Matcher accessMatcher = Pattern.compile("访问码:([a-zA-Z0-9]+)").matcher(shareCode);
accessCode = accessMatcher.find() ? accessMatcher.group(1) : "";
} else {
accessCode = "";
} }
} }
shareCode = shareCode.split("(访问码")[0].trim();
ShareData shareData = new ShareData(shareCode, "0"); ShareData shareData = new ShareData(shareCode, "0");
shareData.setSharePwd(accessCode); shareData.setSharePwd(accessCode);
return shareData; return shareData;
} }

View File

@ -44,7 +44,7 @@ public class UCApi {
private String cookieToken = ""; private String cookieToken = "";
private String ckey = ""; private String ckey = "";
private Map<String, Map<String, Object>> shareTokenCache = new HashMap<>(); private Map<String, Map<String, Object>> shareTokenCache = new HashMap<>();
private String pr = "pr=UCBrowser&fr=pc"; 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 List<String> subtitleExts = Arrays.asList(".srt", ".ass", ".scc", ".stl", ".ttml");
private Map<String, String> saveFileIdCaches = new HashMap<>(); private Map<String, String> saveFileIdCaches = new HashMap<>();
private String saveDirId = null; private String saveDirId = null;
@ -87,7 +87,15 @@ public class UCApi {
cache = Cache.objectFrom(Path.read(getCache())); cache = Cache.objectFrom(Path.read(getCache()));
tokenCache = Cache.objectFrom(Path.read(qrCodeHandler.getCache())); tokenCache = Cache.objectFrom(Path.read(qrCodeHandler.getCache()));
this.cookieToken = tokenCache.getUser().getCookie(); 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); SpiderDebug.log("UC初始化获取到的cookieToken: " + cookieToken);
} }
@ -181,9 +189,9 @@ public class UCApi {
List<String> playFrom = UCApi.get().getPlayFormatList(); List<String> playFrom = UCApi.get().getPlayFormatList();
List<String> playFromtmp = new ArrayList<>(); List<String> playFromtmp = new ArrayList<>();
playFromtmp.add("uc原画"); playFromtmp.add("uc原画");
for (String s : playFrom) { /* for (String s : playFrom) {
playFromtmp.add("uc" + s); playFromtmp.add("uc" + s);
} }*/
List<String> playUrl = new ArrayList<>(); List<String> playUrl = new ArrayList<>();
if (files.isEmpty()) { if (files.isEmpty()) {
@ -217,23 +225,19 @@ public class UCApi {
SpiderDebug.log("flag:" + flag); SpiderDebug.log("flag:" + flag);
String fileId = split[0], fileToken = split[1], shareId = split[2], stoken = split[3]; String fileId = split[0], fileToken = split[1], shareId = split[2], stoken = split[3];
String playUrl = ""; String playUrl = "";
if (flag.contains("uc原画")) {
playUrl = this.getDownload(shareId, stoken, fileId, fileToken, true);
} else {
playUrl = this.getLiveTranscoding(shareId, stoken, fileId, fileToken, flag);
}
SpiderDebug.log("origin playUrl:" + playUrl); SpiderDebug.log("origin playUrl:" + playUrl);
Map<String, String> header = getHeaders(); Map<String, String> header = getHeaders();
header.remove("Host"); header.remove("Host");
header.remove("Content-Type"); header.remove("Content-Type");
if (flag.contains("uc原画")) {
//UCTV 可以直接播放不需要代理 playUrl = this.getDownload(shareId, stoken, fileId, fileToken, true);
if (testVideo(playUrl)) {
SpiderDebug.log("UCTV 可以直接播放,不需要代理" );
return Result.get().url(playUrl).string(); return Result.get().url(playUrl).string();
} else {
playUrl = this.getLiveTranscoding(shareId, stoken, fileId, fileToken, flag);
return Result.get().url(proxyVideoUrl(playUrl, new HashMap<>())).string();
} }
return Result.get().url(proxyVideoUrl(playUrl, header)).octet().header(header).string();
} }
private boolean testVideo(String url) { private boolean testVideo(String url) {

View File

@ -178,25 +178,7 @@ public class UCTokenHandler {
JsonObject resData = Json.safeObject(okResult.getBody()); JsonObject resData = Json.safeObject(okResult.getBody());
String code = resData.get("code").getAsString(); String code = resData.get("code").getAsString();
pathname = "/token"; OkResult okResult1 = getAccessToken(code, false);
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");
postData.put("activity_rect", URLEncoder.encode("{}", "UTF-8"));
postData.put("channel", (String) conf.get("channel"));
postData.put("code", code);
OkResult okResult1 = OkHttp.post(conf.get("codeApi") + pathname, Json.toJson(postData), headers);
if (okResult1.getCode() == 200) { if (okResult1.getCode() == 200) {
@ -208,8 +190,7 @@ public class UCTokenHandler {
SpiderDebug.log("uc Token获取成功" + tokenResData.get("data").getAsJsonObject().get("access_token").getAsString()); SpiderDebug.log("uc Token获取成功" + tokenResData.get("data").getAsJsonObject().get("access_token").getAsString());
//保存到本地 //保存到本地
cache.setTokenUser(User.objectFrom(tokenResData.get("data").getAsJsonObject().get("access_token").getAsString())); cache.setTokenUser(User.objectFrom(Json.toJson(tokenResData.get("data").getAsJsonObject())));
//停止检验线程关闭弹窗 //停止检验线程关闭弹窗
stopService(); stopService();
return result; return result;
@ -226,6 +207,74 @@ public class UCTokenHandler {
platformStates.remove("UC_TOKEN"); platformStates.remove("UC_TOKEN");
return Map.of("status", "EXPIRED"); 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 { public String download(String token, String saveFileId) throws Exception {
SpiderDebug.log("开始下载:" + saveFileId + ";token:" + token); SpiderDebug.log("开始下载:" + saveFileId + ";token:" + token);
@ -266,6 +315,11 @@ public class UCTokenHandler {
OkResult okResult1 = OkHttp.get(API_URL + pathname, params, headers); OkResult okResult1 = OkHttp.get(API_URL + pathname, params, headers);
JsonObject obj = Json.safeObject(okResult1.getBody()); 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(); String downloadUrl = obj.get("data").getAsJsonObject().get("video_info").getAsJsonArray().get(0).getAsJsonObject().get("url").getAsString();
SpiderDebug.log("uc TV 下载文件内容:" + downloadUrl); SpiderDebug.log("uc TV 下载文件内容:" + downloadUrl);
return downloadUrl; return downloadUrl;

View File

@ -1,25 +1,39 @@
package com.github.catvod.api; 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.OkHttp;
import com.github.catvod.net.OkResult; import com.github.catvod.net.OkResult;
import com.github.catvod.utils.Json; 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.JsonElement;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import org.apache.commons.codec.binary.Base64; import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.*; 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.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class YunDrive { public class YunDrive {
private final Pattern regex = Pattern.compile("https://yun\\.139\\.com/shareweb/#/w/i/([^&]+)"); private final Pattern regex = Pattern.compile("https://yun\\.139\\.com/shareweb/#/w/i/([^&]+)");
private final SecretKeySpec secretKey; private final SecretKeySpec secretKey;
@ -76,6 +90,14 @@ public class YunDrive {
matcher = Pattern.compile("https://caiyun\\.139\\.com/m/i\\?([^&]+)").matcher(url); matcher = Pattern.compile("https://caiyun\\.139\\.com/m/i\\?([^&]+)").matcher(url);
finded = matcher.find(); 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); if (finded) linkID = matcher.group(1);
return linkID; return linkID;
@ -169,7 +191,7 @@ public class YunDrive {
for (JsonElement element : response.getAsJsonArray("coLst")) { for (JsonElement element : response.getAsJsonArray("coLst")) {
JsonObject entry = element.getAsJsonObject(); JsonObject entry = element.getAsJsonObject();
if (entry.get("coType").getAsInt() == 3) { if (entry.get("coType").getAsInt() == 3) {
items.add(Map.of("name", entry.get("coName").getAsString(), "contentId", entry.get("coID").getAsString(), "linkID", linkID)); 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")) { } else if (response.has("caLst")) {
@ -198,5 +220,96 @@ public class YunDrive {
return m3u8.split("playlist.m3u8")[0] + resultUrl; 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();
}
} }

View File

@ -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";
}
}

View File

@ -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);
}
}

View File

@ -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);
}
} }

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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
)

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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 = "";
}
}

View File

@ -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>()
}
}
}

View File

@ -2,63 +2,84 @@ package com.github.catvod.spider;
import android.content.Context; import android.content.Context;
import android.text.TextUtils; import android.text.TextUtils;
import com.github.catvod.api.TianyiApi; import com.github.catvod.api.Pan123Api;
import com.github.catvod.crawler.Spider; import com.github.catvod.crawler.Spider;
import com.github.catvod.crawler.SpiderDebug;
import com.github.catvod.utils.Json; import com.github.catvod.utils.Json;
import com.github.catvod.utils.Util; import com.github.catvod.utils.Util;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.ImmutablePair;
import java.util.ArrayList; import java.util.*;
import java.util.List; import java.util.concurrent.ExecutorService;
import java.util.Objects; import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import static com.github.catvod.api.TianyiApi.URL_START; import static com.github.catvod.api.TianyiApi.URL_CONTAIN;
/** /**
* @author ColaMint & Adam & FongMi * @author ColaMint & Adam & FongMi
*/ */
public class Cloud extends Spider { public class Cloud extends Spider {
private Quark quark = null; private Quark quark = null;
private Ali ali = null; /* private Ali ali = null;*/
private UC uc = null; private UC uc = null;
private TianYi tianYi = null; private TianYi tianYi = null;
private YiDongYun yiDongYun = 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 @Override
public void init(Context context, String extend) throws Exception { public void init(Context context, String extend) throws Exception {
JsonObject ext = Json.safeObject(extend); JsonObject ext = Json.safeObject(extend);
quark = new Quark(); quark = new Quark();
uc = new UC(); uc = new UC();
ali = new Ali(); /* ali = new Ali();*/
tianYi = new TianYi(); tianYi = new TianYi();
yiDongYun = new YiDongYun(); yiDongYun = new YiDongYun();
baiDuPan = new BaiDuPan();
pan123 = new Pan123();
boolean first = Objects.nonNull(ext); boolean first = Objects.nonNull(ext);
quark.init(context, first && ext.has("cookie") ? ext.get("cookie").getAsString() : ""); quark.init(context, first && ext.has("cookie") ? ext.get("cookie").getAsString() : "");
uc.init(context, first && ext.has("uccookie") ? ext.get("uccookie").getAsString() : ""); uc.init(context, first && ext.has("uccookie") ? ext.get("uccookie").getAsString() : "");
ali.init(context, first && ext.has("token") ? ext.get("token").getAsString() : ""); /* ali.init(context, first && ext.has("token") ? ext.get("token").getAsString() : "");*/
tianYi.init(context, first && ext.has("tianyicookie") ? ext.get("tianyicookie").getAsString() : ""); tianYi.init(context, first && ext.has("tianyicookie") ? ext.get("tianyicookie").getAsString() : "");
yiDongYun.init(context, ""); yiDongYun.init(context, "");
baiDuPan.init(context, "");
pan123.init(context, "");
} }
@Override @Override
public String detailContent(List<String> shareUrl) throws Exception { public String detailContent(List<String> shareUrl) throws Exception {
if (shareUrl.get(0).matches(Util.patternAli)) { SpiderDebug.log("cloud detailContent shareUrl" + Json.toJson(shareUrl));
/* if (shareUrl.get(0).matches(Util.patternAli)) {
return ali.detailContent(shareUrl); return ali.detailContent(shareUrl);
} else if (shareUrl.get(0).matches(Util.patternQuark)) { } else */
if (shareUrl.get(0).matches(Util.patternQuark)) {
return quark.detailContent(shareUrl); return quark.detailContent(shareUrl);
} else if (shareUrl.get(0).matches(Util.patternUC)) { } else if (shareUrl.get(0).matches(Util.patternUC)) {
return uc.detailContent(shareUrl); return uc.detailContent(shareUrl);
} else if (shareUrl.get(0).startsWith(TianyiApi.URL_START)) { } else if (shareUrl.get(0).contains(URL_CONTAIN)) {
return tianYi.detailContent(shareUrl); return tianYi.detailContent(shareUrl);
} else if (shareUrl.get(0).contains(YiDongYun.URL_START)) { } else if (shareUrl.get(0).contains(YiDongYun.URL_START)) {
return yiDongYun.detailContent(shareUrl); 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; return null;
} }
@Override @Override
public String playerContent(String flag, String id, List<String> vipFlags) throws Exception { public String playerContent(String flag, String id, List<String> vipFlags) throws Exception {
SpiderDebug.log("cloud playerContent flag" + flag + " id" + id);
if (flag.contains("quark")) { if (flag.contains("quark")) {
return quark.playerContent(flag, id, vipFlags); return quark.playerContent(flag, id, vipFlags);
} else if (flag.contains("uc")) { } else if (flag.contains("uc")) {
@ -67,47 +88,105 @@ public class Cloud extends Spider {
return tianYi.playerContent(flag, id, vipFlags); return tianYi.playerContent(flag, id, vipFlags);
} else if (flag.contains("移动")) { } else if (flag.contains("移动")) {
return yiDongYun.playerContent(flag, id, vipFlags); return yiDongYun.playerContent(flag, id, vipFlags);
} else { }/* else {
return ali.playerContent(flag, id, vipFlags); 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) { protected String detailContentVodPlayFrom(List<String> shareLinks) {
List<String> from = new ArrayList<>(); 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; int i = 0;
for (String shareLink : shareLinks) { for (String shareLink : shareLinks) {
i++;
if (shareLink.matches(Util.patternUC)) {
from.add(uc.detailContentVodPlayFrom(List.of(shareLink), i));
} else if (shareLink.matches(Util.patternQuark)) {
from.add(quark.detailContentVodPlayFrom(List.of(shareLink), i));
} else if (shareLink.matches(Util.patternAli)) {
from.add(ali.detailContentVodPlayFrom(List.of(shareLink), i));
} else if (shareLink.startsWith(URL_START)) {
from.add(tianYi.detailContentVodPlayFrom(List.of(shareLink), i));
} else if (shareLink.contains(YiDongYun.URL_START)) {
from.add(yiDongYun.detailContentVodPlayFrom(List.of(shareLink), i));
}
}
return TextUtils.join("$$$", from); int finalI = ++i;
} futures.add(service.submit(() -> {
protected String detailContentVodPlayUrl(List<String> shareLinks) throws Exception { String url = "";
List<String> urls = new ArrayList<>(); String from = "";
for (String shareLink : shareLinks) {
if (shareLink.matches(Util.patternUC)) { if (shareLink.matches(Util.patternUC)) {
urls.add(uc.detailContentVodPlayUrl(List.of(shareLink))); url = uc.detailContentVodPlayUrl(List.of(shareLink));
from = uc.detailContentVodPlayFrom(List.of(shareLink), finalI);
} else if (shareLink.matches(Util.patternQuark)) { } else if (shareLink.matches(Util.patternQuark)) {
urls.add(quark.detailContentVodPlayUrl(List.of(shareLink))); url = quark.detailContentVodPlayUrl(List.of(shareLink));
} else if (shareLink.matches(Util.patternAli)) { from = quark.detailContentVodPlayFrom(List.of(shareLink), finalI);
}/* else if (shareLink.matches(Util.patternAli)) {
urls.add(ali.detailContentVodPlayUrl(List.of(shareLink))); urls.add(ali.detailContentVodPlayUrl(List.of(shareLink)));
} else if (shareLink.startsWith(URL_START)) { } */ else if (shareLink.contains(URL_CONTAIN)) {
urls.add(tianYi.detailContentVodPlayUrl(List.of(shareLink))); url = tianYi.detailContentVodPlayUrl(List.of(shareLink));
from = tianYi.detailContentVodPlayFrom(List.of(shareLink), finalI);
} else if (shareLink.contains(YiDongYun.URL_START)) { } else if (shareLink.contains(YiDongYun.URL_START)) {
urls.add(yiDongYun.detailContentVodPlayUrl(List.of(shareLink))); 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();
} }
} }
return TextUtils.join("$$$", urls);
}
} }

View File

@ -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="
}
}

View File

@ -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();

View File

@ -1,107 +0,0 @@
package com.github.catvod.spider;
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 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;
import com.github.catvod.bean.Class;
import com.github.catvod.utils.Util;
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", "PostmanRuntime/7.36.3");
headers.put("Host", "jable.tv");
headers.put("Postman-Token", "33290483-3c8d-413f-a160-0d3aea9e6f95");
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();
}
}

View File

@ -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();
}
}

View File

@ -1,84 +1,114 @@
package com.github.catvod.spider; package com.github.catvod.spider;
import android.content.Context; import android.content.Context;
import com.github.catvod.bean.Class; import com.github.catvod.bean.Class;
import com.github.catvod.bean.Result; import com.github.catvod.bean.Result;
import com.github.catvod.bean.Vod; 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.*; import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/** /**
* Qile * Qile
*/ */
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);
if (tid.equals("50") || tid.equals("99") || tid.equals("111")) {
List<Vod> list = new ArrayList<>();
String url = siteUrl + String.format("/api/dyTag/list?category_id=%s&page=%s", tid, pg);
Resp resp = Resp.objectFrom(OkHttp.string(url, getHeader()));
for (Data data : resp.getData()) for (Data dataList : data.getDataList()) list.add(dataList.vod(imgDomain));
return Result.get().page().vod(list).string();
} else {
List<Vod> list = new ArrayList<>(); List<Vod> list = new ArrayList<>();
HashMap<String, String> ext = new HashMap<>(); HashMap<String, String> ext = new HashMap<>();
if (extend != null && extend.size() > 0) ext.putAll(extend); if (extend != null && !extend.isEmpty()) ext.putAll(extend);
String cateId = ext.get("cateId") == null ? tid : ext.get("cateId");
String area = ext.get("area") == null ? "0" : ext.get("area"); String area = ext.get("area") == null ? "0" : ext.get("area");
String year = ext.get("year") == null ? "0" : ext.get("year"); String year = ext.get("year") == null ? "0" : ext.get("year");
String by = ext.get("by") == null ? "hot" : ext.get("by"); String by = ext.get("by") == null ? "updata" : ext.get("by");
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); 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())); 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.vod(imgDomain));
return Result.string(Integer.parseInt(pg), Integer.parseInt(pg) + 1, 24, Integer.MAX_VALUE, list); 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);
@ -101,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);
} }
} }

View File

@ -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);
}
}

View File

@ -55,7 +55,7 @@ public class Proxy extends Spider {
/*for (Map.Entry<String, String> entry : params.entrySet()) { /*for (Map.Entry<String, String> entry : params.entrySet()) {
if (!keys.contains(entry.getKey())) header.put(entry.getKey(), entry.getValue()); if (!keys.contains(entry.getKey())) header.put(entry.getKey(), entry.getValue());
}*/ }*/
return ProxyVideo.proxy(url, header); return ProxyVideo.proxyMultiThread(url, header);
} }

View File

@ -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) {

View File

@ -8,7 +8,6 @@ import com.github.catvod.bean.Result;
import com.github.catvod.bean.quark.ShareData; import com.github.catvod.bean.quark.ShareData;
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.utils.Notify;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -55,12 +54,12 @@ public class Quark extends Spider {
}*/ }*/
for (int i = 1; i <= ids.size(); i++) { for (int i = 1; i <= ids.size(); i++) {
playFrom.add(String.format("quark原画#%02d_%02d" ,i , index));
for (String s : QuarkApi.get().getPlayFormatList()) { for (String s : QuarkApi.get().getPlayFormatList()) {
playFrom.add(String.format(Locale.getDefault(), "quark" + s + "#%02d%02d", i, index)); playFrom.add(String.format(Locale.getDefault(), "quark" + s + "#%02d_%02d", i, index));
} }
playFrom.add("quark原画" + i + index);
} }
return TextUtils.join("$$$", playFrom); return TextUtils.join("$$$", playFrom);
} }
@ -71,14 +70,15 @@ public class Quark extends Spider {
* @param ids share_link 集合 * @param ids share_link 集合
* @return 詳情內容視頻播放地址 * @return 詳情內容視頻播放地址
*/ */
public String detailContentVodPlayUrl(List<String> ids) throws Exception { public String detailContentVodPlayUrl(List<String> ids) {
List<String> playUrl = new ArrayList<>(); List<String> playUrl = new ArrayList<>();
for (String id : ids) { for (String id : ids) {
ShareData shareData = QuarkApi.get().getShareData(id); ShareData shareData = QuarkApi.get().getShareData(id);
try { try {
playUrl.add(QuarkApi.get().getVod(shareData).getVodPlayUrl()); playUrl.add(QuarkApi.get().getVod(shareData)==null?"":QuarkApi.get().getVod(shareData).getVodPlayUrl());
} catch (Exception e) { } catch (Exception e) {
SpiderDebug.log("获取播放地址出错:" + e.getMessage()); SpiderDebug.log("获取播放地址出错:" + e.getMessage());
playUrl.add("");
} }
} }
return TextUtils.join("$$$", playUrl); return TextUtils.join("$$$", playUrl);

View File

@ -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\":[]}";
}
}
}

View File

@ -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)
}
}

View File

@ -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))
}
}

View File

@ -1,211 +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.ProxyVideo;
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.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.*;
import java.nio.charset.Charset;
import java.util.*;
public class Supjav extends Spider {
private static final String siteUrl = "https://supjav.com/zh/";
private static final String playUrl = "https://lk1.supremejav.com/";
private HashMap<String, String> getHeaders() {
return getHeaders(siteUrl);
}
private HashMap<String, String> getHeaders(String referer) {
HashMap<String, String> headers = new HashMap<>();
headers.put("Referer", "https://supjav.com/");
headers.put("Host", "supjav.com");
headers.put("User-Agent", Util.CHROME);
return headers;
}
private HashMap<String, String> getTVVideoHeaders(String referer) {
HashMap<String, String> headers = new HashMap<>();
headers.put("Referer", referer);
headers.put("User-Agent", Util.CHROME);
return headers;
}
@Override
public String homeContent(boolean filter) {
List<Vod> list = new ArrayList<>();
List<Class> classes = new ArrayList<>();
Document doc = Jsoup.parse(OkHttp.string(siteUrl, getHeaders()));
for (Element element : doc.select("ul.nav > li > a")) {
String href = element.attr("href");
if (href.split("/").length < 5) continue;
String typeId = href.replace(siteUrl, "");
String typeName = element.text();
classes.add(new Class(typeId, typeName));
}
for (Element element : doc.select("div.post")) {
String src = element.select("img").attr("src");
String data = element.select("img").attr("data-original");
String url = element.select("a").attr("href");
String name = element.select("a").attr("title");
String pic = StringUtils.isEmpty(data) ? src : data;
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) {
List<Vod> list = new ArrayList<>();
Document doc = Jsoup.parse(OkHttp.string(siteUrl + tid + "/page/" + pg, getHeaders()));
for (Element element : doc.select("div.post")) {
String pic = element.select("img").attr("data-original");
String url = element.select("a").attr("href");
String name = element.select("a").attr("title");
String id = url.split("/")[4];
list.add(new Vod(id, name, pic));
}
return Result.string(list);
}
@Override
public String detailContent(List<String> ids) {
Document doc = Jsoup.parse(OkHttp.string(siteUrl.concat(ids.get(0)), getHeaders()));
String name = doc.select("div.post-meta > img").attr("alt");
String img = doc.select("div.post-meta > img").attr("src");
String type = doc.select("p.cat > a").text();
String director = "", actor = "";
for (Element p : doc.select("div.cats > p")) {
if (p.select("span").text().contains("Maker")) {
director = p.select("a").text();
}
if (p.select("span").text().contains("Cast")) {
actor = p.select("a").text();
}
}
Vod vod = new Vod();
vod.setVodId(ids.get(0));
vod.setVodPic(img);
vod.setVodName(name);
vod.setVodActor(actor);
vod.setVodDirector(director);
vod.setTypeName(type);
Map<String, String> sites = new LinkedHashMap<>();
Elements sources = doc.select("a.btn-server");
for (int i = 0; i < sources.size(); i++) {
Element source = sources.get(i);
String sourceName = source.text();
if (sourceName.equals("TV")) continue;
String sourceUrl = source.attr("data-link");
sites.put(sourceName, "播放" + "$" + sourceUrl);
}
if (!sites.isEmpty()) {
vod.setVodPlayFrom(StringUtils.join(sites.keySet(), "$$$"));
vod.setVodPlayUrl(StringUtils.join(sites.values(), "$$$"));
}
return Result.string(vod);
}
@Override
public String searchContent(String key, boolean quick) throws UnsupportedEncodingException {
List<Vod> list = new ArrayList<>();
Document doc = Jsoup.parse(OkHttp.string(siteUrl.concat("?s=").concat(URLEncoder.encode(key, "UTF-8")), getHeaders()));
for (Element element : doc.select("div.post")) {
String pic = element.select("img").attr("data-original");
String url = element.select("a").attr("href");
String name = element.select("a").attr("title");
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 URISyntaxException, IOException {
String redirect = OkHttp.getLocation(playUrl + "supjav.php?c=" + new StringBuilder(id).reverse(), getTVVideoHeaders(playUrl + "supjav.php?l=" + id + "&bg=undefined"));
switch (flag) {
case "TV":
return parseTV(redirect);
case "ST":
return parseST(redirect);
case "FST":
return parseFST(redirect);
case "VOE":
return parseVOE(redirect);
default:
return Result.get().url(id).parse().string();
}
}
private String parseVOE(String redirect) {
String data = OkHttp.string(redirect, getTVVideoHeaders(playUrl));
redirect = Util.findByRegex("window.location.href = '(.*?)';", data, 1);
data = OkHttp.string(redirect, getTVVideoHeaders(playUrl));
String url=Util.findByRegex("prompt\\(\"Node\",(.*?)\\);", data, 1).trim().replace("\"", "");
return Result.get().url(ProxyVideo.buildCommonProxyUrl(url, Util.webHeaders(redirect))).header(getHeaders(redirect)).string();
}
private String parseFST(String redirect) {
String data = OkHttp.string(redirect, getTVVideoHeaders(playUrl));
return Result.get().url(Util.findByRegex("file:\"(.*?)\"}]", data, 1)).header(getHeaders(redirect)).string();
}
private String parseTV(String redirect) throws MalformedURLException {
String data = OkHttp.string(redirect, getTVVideoHeaders(Util.getHost(redirect)));
return Result.get().url(Util.getVar(data, "urlPlay")).header(getTVVideoHeaders(Util.getHost(redirect))).string();
}
private String parseST(String redirect) throws IOException {
String data = OkHttp.string(redirect, getTVVideoHeaders(playUrl));
String robot = Jsoup.parse(data).getElementById("robotlink").text();
robot = robot.substring(0, robot.indexOf("&token=") + 7);
for (String text : data.split("&token=")) {
if (!text.contains("').substring(")) continue;
robot = "https:/" + robot + text.split("'")[0] + "&stream=1";
String url = OkHttp.getLocation(robot, getTVVideoHeaders(redirect));
return Result.get().url(ProxyVideo.buildCommonProxyUrl(url, Util.webHeaders(robot))).header(getHeaders(redirect)).string();
}
return "";
}
private String parseDS(String redirect) throws URISyntaxException, IOException {
String host = "https://" + Util.getHost(redirect);
redirect = host + OkHttp.getLocation(redirect, getTVVideoHeaders(playUrl));
String data = OkHttp.string(redirect, getHeaders());
for (String text : data.split("'")) {
if (!text.startsWith("/pass_md5/")) continue;
String token = text.split("/")[3];
String url = OkHttp.string(host + text, getHeaders(redirect));
url = url + getDSRnd() + "?token=" + token + "&expiry=" + System.currentTimeMillis();
return Result.get().url(url).header(getHeaders(redirect)).string();
}
return "";
}
private String getDSRnd() {
StringBuilder sb = new StringBuilder();
String t = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
for (int o = 0; o < 10; o++)
sb.append(t.charAt((int) Math.floor(Math.random() * t.length())));
return sb.toString();
}
}

View File

@ -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)
}
}

View File

@ -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)
}
}

View File

@ -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)
}
}

View File

@ -1,56 +0,0 @@
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.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
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 TgSearch extends Cloud {
private final String URL = "https://tg.252035.xyz/";
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 {
String url = URL + "?channelUsername=tianyirigeng,tyypzhpd,XiangxiuNB,yunpanpan,kuakeyun,zaihuayun,Quark_Movies,alyp_4K_Movies,vip115hot,yunpanshare,dianyingshare&keyword=" + URLEncoder.encode(key, Charset.defaultCharset().name());
List<Vod> list = new ArrayList<>();
String html = OkHttp.string(url, getHeader());
String[] arr = html.split(":I");
if (arr.length > 0) {
for (String s : arr) {
Document doc = Jsoup.parse(Util.findByRegex("链接(.*)", s, 1));
String id = doc.select(" a").attr("href");
String name = Util.findByRegex("名称(.*)描述", s, 1).replace("","").replace(":","");
String desc = Util.findByRegex("描述(.*) 链接", s, 1).replace("","").replace(":","");
list.add(new Vod(id, name, "", desc));
}
}
return Result.string(list);
}
}

View File

@ -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)
}
}

View File

@ -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 "";
}
}

View File

@ -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 TgSearch139 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 "";
}
}

View File

@ -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 TgSearch189 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 "";
}
}

View File

@ -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 TgSearchBaidu 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 "";
}
}

View File

@ -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 TgSearchQuark 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 "";
}
}

View File

@ -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 TgbaiduSearch : 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=BaiduCloudDisk,bdwpzhpd,Baidu_Netdisk&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)
}
}

View File

@ -55,7 +55,7 @@ public class TianYi extends Spider {
for (int i = 1; i <= ids.size(); i++) { for (int i = 1; i <= ids.size(); i++) {
for (String s : TianyiApi.get().getPlayFormatList()) { for (String s : TianyiApi.get().getPlayFormatList()) {
playFrom.add(String.format(Locale.getDefault(), "天意" + s + "#%02d%02d", i, index)); playFrom.add(String.format(Locale.getDefault(), "天意" + s + "#%02d_%02d", i, index));
} }
// playFrom.add("天意" + i + index); // playFrom.add("天意" + i + index);
@ -69,7 +69,7 @@ public class TianYi extends Spider {
* @param ids share_link 集合 * @param ids share_link 集合
* @return 詳情內容視頻播放地址 * @return 詳情內容視頻播放地址
*/ */
public String detailContentVodPlayUrl(List<String> ids) throws Exception { public String detailContentVodPlayUrl(List<String> ids) {
List<String> playUrl = new ArrayList<>(); List<String> playUrl = new ArrayList<>();
for (String id : ids) { for (String id : ids) {
ShareData shareData = TianyiApi.get().getShareData(id, ""); ShareData shareData = TianyiApi.get().getShareData(id, "");
@ -77,6 +77,7 @@ public class TianYi extends Spider {
playUrl.add(TianyiApi.get().getVod(shareData).getVodPlayUrl()); playUrl.add(TianyiApi.get().getVod(shareData).getVodPlayUrl());
} catch (Exception e) { } catch (Exception e) {
SpiderDebug.log("获取播放地址出错:" + e.getMessage()); SpiderDebug.log("获取播放地址出错:" + e.getMessage());
playUrl.add("");
} }
} }
return TextUtils.join("$$$", playUrl); return TextUtils.join("$$$", playUrl);

View File

@ -56,10 +56,10 @@ public class UC extends Spider {
for (int i = 1; i <= ids.size(); i++) { for (int i = 1; i <= ids.size(); i++) {
for (String s : UCApi.get().getPlayFormatList()) { /* for (String s : UCApi.get().getPlayFormatList()) {
playFrom.add(String.format(Locale.getDefault(), "uc" + s + "#%02d%02d", i, index)); playFrom.add(String.format(Locale.getDefault(), "uc" + s + "#%02d%02d", i, index));
} }*/
playFrom.add("uc原画" + i + index); playFrom.add("uc原画" + i + index);
} }
return TextUtils.join("$$$", playFrom); return TextUtils.join("$$$", playFrom);
@ -71,11 +71,17 @@ public class UC extends Spider {
* @param ids share_link 集合 * @param ids share_link 集合
* @return 詳情內容視頻播放地址 * @return 詳情內容視頻播放地址
*/ */
public String detailContentVodPlayUrl(List<String> ids) throws Exception { public String detailContentVodPlayUrl(List<String> ids) {
List<String> playUrl = new ArrayList<>(); List<String> playUrl = new ArrayList<>();
for (String id : ids) { for (String id : ids) {
try {
ShareData shareData = UCApi.get().getShareData(id); ShareData shareData = UCApi.get().getShareData(id);
playUrl.add(UCApi.get().getVod(shareData).getVodPlayUrl()); playUrl.add(UCApi.get().getVod(shareData)==null?"":UCApi.get().getVod(shareData).getVodPlayUrl());
}catch (Exception e){
SpiderDebug.log("获取播放地址出错:" + e.getMessage());
playUrl.add("");
}
} }
return TextUtils.join("$$$", playUrl); return TextUtils.join("$$$", playUrl);
} }

View File

@ -49,7 +49,20 @@ public class YiDongYun extends Spider {
} }
} }
builder.append("移动", list); builder.append("移动(极速)", list);
List<Vod.VodPlayBuilder.PlayUrl> list2 = new ArrayList<>();
for (String s : result.keySet()) {
vodName = s;
for (Map<String, String> stringStringMap : result.get(s)) {
Vod.VodPlayBuilder.PlayUrl playUrl = new Vod.VodPlayBuilder.PlayUrl();
playUrl.url = stringStringMap.get("path") + "++" + stringStringMap.get("linkID");
playUrl.name = stringStringMap.get("name");
list2.add(playUrl);
}
}
builder.append("移动(原画)", list2);
Vod.VodPlayBuilder.BuildResult buildResult = builder.build(); Vod.VodPlayBuilder.BuildResult buildResult = builder.build();
Vod vod = new Vod(); Vod vod = new Vod();
vod.setVodId(ids.get(0)); vod.setVodId(ids.get(0));
@ -65,11 +78,8 @@ public class YiDongYun extends Spider {
@Override @Override
public String playerContent(String flag, String id, List<String> vipFlags) throws Exception { public String playerContent(String flag, String id, List<String> vipFlags) throws Exception {
String contentId = id.split("\\+\\+")[0];
String linkID = id.split("\\+\\+")[1]; return YunDrive.get().playerContent(id.split("\\+\\+"), flag);
String playContent = YunDrive.get().fetchPlayUrl(contentId, linkID);
SpiderDebug.log("playContent:" + playContent);
return Result.get().url(playContent).octet().string();
} }
/** /**
@ -83,7 +93,10 @@ public class YiDongYun extends Spider {
List<String> playFrom = new ArrayList<>(); List<String> playFrom = new ArrayList<>();
int i = 0; int i = 0;
for (String id : ids) { for (String id : ids) {
playFrom.add("移动" + i++); i++;
playFrom.add(String.format("移动(极速)#%2d", i));
playFrom.add(String.format("移动(原画)#%2d", i));
} }
@ -96,10 +109,17 @@ public class YiDongYun extends Spider {
* @param ids share_link 集合 * @param ids share_link 集合
* @return 詳情內容視頻播放地址 * @return 詳情內容視頻播放地址
*/ */
public String detailContentVodPlayUrl(List<String> ids) throws Exception { public String detailContentVodPlayUrl(List<String> ids) {
List<String> playUrl = new ArrayList<>(); List<String> playUrl = new ArrayList<>();
for (String id : ids) { for (String id : ids) {
try {
playUrl.add(getVod(List.of(id)).getVodPlayUrl()); playUrl.add(getVod(List.of(id)).getVodPlayUrl());
}catch (Exception e) {
SpiderDebug.log("获取播放地址出错:" + e.getMessage());
playUrl.add("");
}
} }
return TextUtils.join("$$$", playUrl); return TextUtils.join("$$$", playUrl);
} }

View File

@ -120,6 +120,8 @@ public class YunPanBa extends Cloud {
shareLinks.add(element.attr("href").trim()); shareLinks.add(element.attr("href").trim());
} else if (element.attr("href").matches(Util.patternAli)) { } else if (element.attr("href").matches(Util.patternAli)) {
shareLinks.add(element.attr("href").trim()); shareLinks.add(element.attr("href").trim());
} else if (element.attr("href").startsWith(BaiDuPan.URL_START)) {
shareLinks.add(element.attr("href").trim());
} }
} }

View File

@ -0,0 +1,362 @@
package com.github.catvod.utils
import com.github.catvod.crawler.SpiderDebug
import java.io.BufferedOutputStream
import java.io.BufferedReader
import java.io.IOException
import java.io.InputStreamReader
import java.net.ServerSocket
import java.net.Socket
import java.net.URLDecoder
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import java.util.concurrent.atomic.AtomicBoolean
import kotlin.text.Charsets.UTF_8
class AdvancedHttpServer(private val port: Int) {
private val serverSocket: ServerSocket
private val threadPool: ExecutorService
private var isRunning = false
private val routes = mutableMapOf<String, (Request, Response) -> Unit>()
init {
serverSocket = ServerSocket(port)
threadPool = Executors.newFixedThreadPool(10)
}
fun addRoutes(path: String, handler: (Request, Response) -> Unit) {
routes[path] = handler
}
fun start() {
isRunning = true
SpiderDebug.log("服务器已启动,监听端口: $port")
while (isRunning) {
try {
val clientSocket = serverSocket.accept()
threadPool.execute { handleRequest(clientSocket) }
} catch (e: IOException) {
e.printStackTrace();SpiderDebug.log("出错:" + e.message)
if (isRunning) e.printStackTrace();SpiderDebug.log("出错:" + e.message)
}
}
}
private fun handleRequest(clientSocket: Socket) {
try {
val reader = BufferedReader(InputStreamReader(clientSocket.inputStream, UTF_8))
val writer = BufferedOutputStream(clientSocket.outputStream)
// 解析请求行
val requestLine = reader.readLine() ?: ""
SpiderDebug.log("requestLine: $requestLine")
val (method, path, _) = parseRequestLine(requestLine)
// 解析路径和查询参数
val (basePath, queryParams) = parsePath(path)
// 读取请求头
val headers = mutableMapOf<String, String>()
var line: String?
while (reader.readLine().also { line = it } != null && line!!.isNotEmpty()) {
val colonIndex = line!!.indexOf(':')
if (colonIndex > 0) {
headers[line!!.substring(0, colonIndex).trim()] =
line!!.substring(colonIndex + 1).trim()
}
}
// 解析请求体参数
val contentLength = headers["Content-Length"]?.toIntOrNull() ?: 0
val requestBody = if (contentLength > 0) {
buildString {
repeat(contentLength) {
val char = reader.read().takeIf { it != -1 }?.toChar() ?: return@buildString
append(char)
}
}
} else ""
val contentType = headers["Content-Type"] ?: ""
val bodyParams = parseRequestBody(contentType, requestBody)
// 创建请求对象
val request = Request(
method = method,
path = basePath,
queryParams = queryParams,
headers = headers,
body = requestBody,
bodyParams = bodyParams
)
// 创建响应处理器
val response = Response(writer)
// 路由处理
routeRequest(request, response)
response.flush()
} catch (e: IOException) {
e.printStackTrace()
SpiderDebug.log("出错:" + e.message)
} finally {
clientSocket.close()
}
}
private fun routeRequest(request: Request, response: Response) {
val route = routes[request.path]
if (route == null) {
handleNotFound(response)
} else {
route.invoke(request, response)
}
}
private fun handleRoot(request: Request, response: Response) {
response.setContentType("text/html")
response.start()
response.write("<html><body>")
response.write("<h1>高级HTTP服务器</h1>")
response.write("<p>支持参数解析和分块响应</p>")
response.write("<h2>功能演示</h2>")
response.write("<ul>")
response.write("<li><a href=\"/echo?param1=value1&param2=value2\">Echo参数</a></li>")
response.write("<li><a href=\"/stream\">流式响应</a></li>")
response.write("<li><a href=\"/slow\">服务器推送事件</a></li>")
response.write("</ul>")
// 演示表单提交
response.write("<h2>表单提交测试</h2>")
response.write("<form action=\"/echo\" method=\"post\">")
response.write("Name: <input type=\"text\" name=\"name\"><br>")
response.write("Age: <input type=\"number\" name=\"age\"><br>")
response.write("<input type=\"submit\" value=\"提交\">")
response.write("</form>")
response.write("</body></html>")
}
private fun handleEcho(request: Request, response: Response) {
response.setContentType("text/html")
response.start()
response.write("<html><body>")
response.write("<h1>Echo服务</h1>")
// 输出请求信息
response.write("<h2>请求信息</h2>")
response.write("<p>方法: ${request.method}</p>")
response.write("<p>路径: ${request.path}</p>")
// 输出查询参数
response.write("<h2>查询参数</h2>")
response.write("<ul>")
if (request.queryParams.isEmpty()) {
response.write("<li>无</li>")
} else {
request.queryParams.forEach { (key, value) ->
response.write("<li>$key: $value</li>")
}
}
response.write("</ul>")
// 输出请求头
response.write("<h2>请求头</h2>")
response.write("<ul>")
request.headers.forEach { (key, value) ->
response.write("<li>$key: $value</li>")
}
response.write("</ul>")
// 输出请求体
response.write("<h2>请求体</h2>")
response.write("<pre>${request.body}</pre>")
// 输出解析后的参数
response.write("<h2>解析后的参数</h2>")
response.write("<ul>")
if (request.bodyParams.isEmpty()) {
response.write("<li>无</li>")
} else {
request.bodyParams.forEach { (key, value) ->
response.write("<li>$key: $value</li>")
}
}
response.write("</ul>")
response.write("</body></html>")
}
private fun handleStreamResponse(response: Response) {
response.setContentType("text/plain")
response.start()
for (i in 1..5) {
response.write("这是第 $i 部分内容\n")
Thread.sleep(500)
}
}
private fun handleSlowResponse(response: Response) {
response.setContentType("text/event-stream")
response.setHeader("Cache-Control", "no-cache")
response.setHeader("Connection", "keep-alive")
response.start()
for (i in 1..10) {
response.write("data: 消息 $i\n\n")
response.flush()
Thread.sleep(1000)
}
}
private fun handleNotFound(response: Response) {
response.setStatusCode(404)
response.setContentType("text/html")
response.start()
response.write("<html><body><h1>404 Not Found</h1></body></html>")
}
private fun parseRequestLine(requestLine: String): Triple<String, String, String> {
val parts = requestLine.split(" ", limit = 3)
return when {
parts.size >= 3 -> Triple(parts[0], parts[1], parts[2])
parts.size == 2 -> Triple(parts[0], parts[1], "HTTP/1.1")
else -> Triple("GET", "/", "HTTP/1.1")
}
}
private fun parsePath(path: String): Pair<String, Map<String, String>> {
val queryIndex = path.indexOf('?')
return if (queryIndex >= 0) {
Pair(path.substring(0, queryIndex), parseQueryString(path.substring(queryIndex + 1)))
} else {
Pair(path, emptyMap())
}
}
private fun parseQueryString(queryString: String): Map<String, String> {
return queryString.split("&").filter { it.isNotEmpty() }.map { param ->
val equalsIndex = param.indexOf('=')
if (equalsIndex >= 0) {
val key = URLDecoder.decode(param.substring(0, equalsIndex), "UTF-8")
val value = URLDecoder.decode(param.substring(equalsIndex + 1), "UTF-8")
key to value
} else {
URLDecoder.decode(param, "UTF-8") to ""
}
}.toMap()
}
private fun parseRequestBody(contentType: String, body: String): Map<String, String> {
return when {
contentType.contains("application/x-www-form-urlencoded") -> parseQueryString(body)
contentType.contains("multipart/form-data") -> parseMultipartFormData(contentType, body)
else -> emptyMap()
}
}
private fun parseMultipartFormData(contentType: String, body: String): Map<String, String> {
// 简化的multipart/form-data解析实际实现需要处理boundary等
return emptyMap()
}
fun stop() {
isRunning = false
threadPool.shutdown()
serverSocket.close()
}
data class Request(
val method: String,
val path: String,
val queryParams: Map<String, String>,
val headers: Map<String, String>,
val body: String,
val bodyParams: Map<String, String>
)
class Response(private val writer: BufferedOutputStream) {
private val headers = mutableMapOf<String, String>()
private var contentType = "text/plain"
private var statusCode = 200
private var started = AtomicBoolean(false)
fun setContentType(contentType: String) {
this.contentType = contentType
}
fun setHeader(name: String, value: String) {
headers[name] = value
}
fun setStatusCode(statusCode: Int) {
this.statusCode = statusCode
}
private val codeMap = mutableMapOf<Int, String>(
206 to "Partial Content",
200 to "OK",
404 to "NOT FOUND",
400 to "BAD REQUEST",
401 to "UNAUTHORIZED",
403 to "FORBIDDEN",
405 to "METHOD NOT ALLOWED",
408 to "REQUEST TIMEOUT",
413 to "PAYLOAD TOO LARGE",
414 to "URI TOO LONG",
415 to "UNSUPPORTED MEDIA TYPE",
429 to "TOO MANY REQUESTS",
500 to "INTERNAL SERVER ERROR",
501 to "NOT IMPLEMENTED",
503 to "SERVICE UNAVAILABLE",
504 to "GATEWAY TIMEOUT",
505 to "HTTP VERSION NOT SUPPORTED",
507 to "INSUFFICIENT STORAGE",
511 to "NETWORK AUTHENTICATION REQUIRED"
)
fun start() {
if (started.compareAndSet(false, true)) {
writer.write("HTTP/1.1 $statusCode ${codeMap[statusCode]}\r\n".toByteArray(UTF_8))
writer.write("Content-Type: $contentType; charset=utf-8\r\n".toByteArray(UTF_8))
headers.forEach { (name, value) ->
writer.write("$name: $value\r\n".toByteArray(UTF_8))
}
writer.write("\r\n".toByteArray(UTF_8))
writer.flush()
}
}
fun write(content: ByteArray) {
if (!started.get()) {
start()
}
writer.write(content)
}
fun write(content: String) {
if (!started.get()) {
start()
}
writer.write(content.toByteArray(UTF_8))
}
fun flush() {
writer.flush()
}
}
}

View File

@ -0,0 +1,25 @@
package com.github.catvod.utils;
import java.util.List;
public class DownloadInfo {
private List<long[]> parts;
private String url;
public List<long[]> getParts() {
return parts;
}
public void setParts(List<long[]> parts) {
this.parts = parts;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}

View File

@ -0,0 +1,173 @@
package com.github.catvod.utils
import com.github.catvod.crawler.SpiderDebug
import com.github.catvod.net.OkHttp
import com.github.catvod.utils.ProxyVideo.getMimeType
import com.github.catvod.utils.ProxyVideo.parseRange
import kotlinx.coroutines.*
import okhttp3.Response
import org.apache.commons.lang3.StringUtils
import java.io.InputStream
import java.io.SequenceInputStream
import java.util.*
import kotlin.math.min
object DownloadMT {
private val THREAD_NUM: Int = 16
private val infos = mutableMapOf<String, Array<Any>>();
fun proxyMultiThread(url: String, headers: Map<String, String>): Array<out Any?>? = runBlocking {
proxyAsync(url, headers)
}
/**
* 获取是否分片信息顺带请求一个1MB块
*/
@Throws(java.lang.Exception::class)
fun getInfo(url: String?, headers: Map<String, String>): Array<Any> {
val newHeaders: MutableMap<String, String> = java.util.HashMap(headers)
newHeaders["Range"] = "bytes=0-" + (1024 * 1024 - 1)
newHeaders["range"] = "bytes=0-" + (1024 * 1024 - 1)
val info = ProxyVideo.proxy(url, newHeaders)
return info
}
private suspend fun proxyAsync(url: String, headers: Map<String, String>): Array<out Any?>? {
/* val service = Executors.newFixedThreadPool(THREAD_NUM)*/
SpiderDebug.log("--proxyMultiThread: THREAD_NUM: $THREAD_NUM")
try {
//缓存避免每次都请求total等信息
var info = infos[url]
//第一次请求请求是否支持range顺带返回一个1MB块
if (info == null) {
infos.clear()
info = CoroutineScope(Dispatchers.IO).async { getInfo(url, headers) }.await()
infos[url] = info/* //支持分片先返回这个1MB块
if (info[0] as Int == 206) {
return info
}*/
}
val code = info[0] as Int
SpiderDebug.log("-----------code:$code")
if (code != 206) {
return ProxyVideo.proxy(url, headers)
}
val resHeader = info[3] as MutableMap<String, String>
val contentRange =
if (StringUtils.isAllBlank(resHeader["Content-Range"])) resHeader["content-range"] else resHeader["Content-Range"]
SpiderDebug.log("--contentRange:$contentRange")
//文件总大小
val total = StringUtils.split(contentRange, "/")[1]
SpiderDebug.log("--文件总大小:$total")
//如果文件太小,也不走代理
/* if (total.toLong() < 1024 * 1024 * 100) {
return proxy(url, headers)
}*/
var range = if (StringUtils.isAllBlank(headers["range"])) headers["Range"] else headers["range"]
if (StringUtils.isAllBlank(range)) range = "bytes=0-";
SpiderDebug.log("---proxyMultiThread,Range:$range")
val rangeObj = parseRange(
range!!
)
//没有range,无需分割
val partList = generatePart(rangeObj, total)
// 存储执行结果的List
val jobs = mutableListOf<Deferred<InputStream>>()
val inputStreams = mutableListOf<InputStream>()
for ((index, part) in partList.withIndex()) {
val newRange = "bytes=" + part[0] + "-" + part[1]
val headerNew: MutableMap<String, String> = HashMap(headers)
headerNew["range"] = newRange
headerNew["Range"] = newRange
jobs += CoroutineScope(Dispatchers.IO).async {
val res = downloadRange(url, headerNew)
SpiderDebug.log(
("---第" + index + "块下载完成" + ";Content-Range:" + (res?.headers())?.get(
"Content-Range"
))
)
res?.body()!!.byteStream()
}
}
inputStreams += jobs.awaitAll()
var contentType: String? = ""
// SpiderDebug.log(" ++proxy res data:" + Json.toJson(response.body()));
contentType = resHeader["Content-Type"]
if (StringUtils.isAllBlank(contentType)) {
contentType = resHeader["content-type"]
}
if (StringUtils.isAllBlank(contentType) && StringUtils.isNoneBlank(resHeader["Content-Disposition"])) {
contentType = getMimeType(resHeader["Content-Disposition"])
}
/* respHeaders.put("Access-Control-Allow-Credentials", "true");
respHeaders.put("Access-Control-Allow-Origin", "*");*/
resHeader["Content-Length"] = (partList[THREAD_NUM - 1][1] - partList[0][0] + 1).toString()
resHeader.remove("content-length")
resHeader["Content-Range"] = String.format(
"bytes %s-%s/%s", partList[0][0], partList[THREAD_NUM - 1][1], total
)
resHeader.remove("content-range")
SpiderDebug.log("----proxy res contentType:$contentType")
// SpiderDebug.log("++proxy res body:" + response.body());
SpiderDebug.log("----proxy res respHeaders:" + Json.toJson(resHeader))
return arrayOf(
206, contentType, SequenceInputStream(Vector(inputStreams).elements()), resHeader
)
} catch (e: Exception) {
SpiderDebug.log("proxyMultiThread error:" + e.message)
e.printStackTrace()
return null
}
}
fun generatePart(rangeObj: Map<String?, String>, total: String): List<LongArray> {
val totalSize = total.toLong()
//超过10GB分块是32Mb不然是16MB
val partSize = if (totalSize > 1024L * 1024L * 1024L * 10L) 1024 * 1024 * 8 * 4L else 1024 * 1024 * 8 * 2L
var start = rangeObj["start"]!!.toLong()
var end = if (StringUtils.isAllBlank(rangeObj["end"])) start + partSize else rangeObj["end"]!!.toLong()
end = min(end.toDouble(), (totalSize - 1).toDouble()).toLong()
val length = end - start + 1
val size = length / THREAD_NUM
val partList: MutableList<LongArray> = ArrayList()
for (i in 0..<THREAD_NUM) {
val partEnd = min((start + size).toDouble(), end.toDouble()).toLong()
partList.add(longArrayOf(start, partEnd))
start = partEnd + 1
}
return partList
}
private fun downloadRange(
url: String, headerNew: MutableMap<String, String>
): Response? = OkHttp.newCall(url, headerNew)
}

View File

@ -0,0 +1,168 @@
package com.github.catvod.utils;
import com.github.catvod.crawler.SpiderDebug;
import com.github.catvod.net.OkHttp;
import okhttp3.Response;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicInteger;
public class DownloadService {
private static final int THREAD_NUM = 16;
//最小分片按这个下载
private static final int PER_PIECE = 1024 * 1024;
//按照这个分割视频每一个部分多线程下载下载完下载下一个部分
private static final int PER_PART = 1024 * 1024 * THREAD_NUM;
private List<Future<Response>> results = new ArrayList<Future<Response>>();
private RandomAccessFile file = null;
private List<long[]> parts;
private AtomicInteger index = new AtomicInteger(0);
private static class Loader {
static volatile DownloadService INSTANCE = new DownloadService();
}
public static DownloadService get() {
return DownloadService.Loader.INSTANCE;
}
/**
* 下载链接
*/
private String url;
/**
* 下载信息
*/
private DownloadInfo downloadInfo;
public void submitDownload(String url, Map<String, String> headers, long length) {
if (url.equals(this.url)) {
SpiderDebug.log("url相同任务已存在");
} else {
this.url = url;
parts = generatePart(length);
downloadInfo = new DownloadInfo();
downloadInfo.setParts(parts);
downloadInfo.setUrl(url);
new Thread(new Runnable() {
@Override
public void run() {
ExecutorService service = Executors.newFixedThreadPool(THREAD_NUM);
for (long[] part : parts) {
String newRange = "bytes=" + part[0] + "-" + part[1];
SpiderDebug.log("下载开始" + ";newRange:" + newRange);
Map<String, String> headerNew = new HashMap<>(headers);
headerNew.put("range", newRange);
headerNew.put("Range", newRange);
Future<Response> result = service.submit(() -> {
try {
part[2] = 1;
return OkHttp.newCall(url, headerNew);
} catch (Exception e) {
throw new RuntimeException(e);
}
});
results.add(result);
}
try {
file = new RandomAccessFile(Path.tv() + File.separator + Util.MD5(url) + ".mp4", "rw");
} catch (FileNotFoundException e) {
SpiderDebug.log("创建文件失败");
throw new RuntimeException(e);
}
while (index.get() < results.size()) {
Response response = null;
try {
response = results.get(index.get()).get();
file.seek(parts.get(index.get())[0]);
file.write(response.body().bytes());
index.addAndGet(1);
} catch (ExecutionException e) {
throw new RuntimeException(e);
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
service.shutdown();
}
}).start();
}
}
/**
* 返回信息
*
* @param start 开始
* @return
*/
public Object[] getDownloadBytes(long start) throws ExecutionException, InterruptedException {
long partIndex = start / PER_PART;
while (parts.get((int) partIndex)[2] == 0) {
index.set((int) partIndex);
}
try {
file.seek(start);
byte[] bytes = new byte[Math.toIntExact(parts.get((int) partIndex)[1] - start + 1)];
file.read(bytes);
return new Object[]{bytes,start,parts.get((int) partIndex)[1] };
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* 分割为需要下载的部分
*
* @param total 文件总大小
* @return
*/
private static List<long[]> generatePart(long total) {
SpiderDebug.log("generatePart.total:" + total);
long start = 0;
long end = total - 1;
//分割为多少个部分
long size = total / PER_PART;
SpiderDebug.log("generatePart.size:" + size);
List<long[]> partList = new ArrayList<>();
for (int i = 0; i < size * THREAD_NUM; i++) {
long partEnd = Math.min(start + PER_PIECE, end);
partList.add(new long[]{start, partEnd, 0});
start = partEnd + 1;
}
//最后多出分为一块下载
if (start <= end) {
partList.add(new long[]{start, end, 0});
}
return partList;
}
}

View File

@ -0,0 +1,525 @@
package com.github.catvod.utils
import com.github.catvod.crawler.SpiderDebug
import com.github.catvod.net.OkHttp
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import java.io.ByteArrayInputStream
object ProxyServer {
private val THREAD_NUM = Runtime.getRuntime().availableProcessors()
private val partSize = 1024 * 1024
private var port = 12345
private var httpServer: AdvancedHttpServer? = null
private val infos = mutableMapOf<String, MutableMap<String, MutableList<String>>>();
private val urlMap = mutableMapOf<String, String>();
private val headerMap = mutableMapOf<String, Map<String, String>>();
fun stop() {
httpServer?.stop()
}
fun start() {
do {
try {
httpServer = AdvancedHttpServer(port)
httpServer?.addRoutes("/") { _, response ->
run {
response.setContentType("text/html")
response.start()
response.write("Hello, world!")
}
};
httpServer?.addRoutes("/proxy") { req, response ->
try {
run {
val key = req.queryParams["key"];
val url = urlMap[key]
val header = headerMap[key]
if (url != null && header != null) {
proxyAsync(url, header, req, response)
}
}
} catch (e: Exception) {
SpiderDebug.log("代理视频出错:" + e.message)
}
}
httpServer?.start()
} catch (e: Exception) {
e.printStackTrace()
SpiderDebug.log("启动服务出错:" + e.message)
port++
httpServer?.stop()
}
} while (port < 20000)
SpiderDebug.log("启动服务 on $port")
}
private fun proxyAsync(
url: String,
headers: Map<String, String>,
request: AdvancedHttpServer.Request,
response: AdvancedHttpServer.Response
) {
runBlocking {
val channels = List(THREAD_NUM) { Channel<ByteArray>() }
SpiderDebug.log("--proxyAsync url: $url")
SpiderDebug.log("--proxyAsync headers: ${Json.toJson(headers)}")
try {
SpiderDebug.log("--proxyMultiThread: THREAD_NUM: $THREAD_NUM")
var rangeHeader = request.headers["Range"]
//没有range头
if (rangeHeader.isNullOrEmpty()) {
// 处理初始请求
rangeHeader = "bytes=0-"
}
headers.toMutableMap().apply {
put("Range", rangeHeader)
}
// 解析范围请求
val (startPoint, endPoint) = parseRangePoint(
rangeHeader
)
//缓存response header
var info = infos[url]
if (info == null) {
info = getInfo(url, headers)
infos[url] = info
}
SpiderDebug.log("startPoint: $startPoint; endPoint: $endPoint")
val contentLength = getContentLength(info)
SpiderDebug.log("contentLength: $contentLength")
val finalEndPoint = if (endPoint == -1L) contentLength - 1 else endPoint
response.setContentType("text/html")
response.setHeader("Connection", "keep-alive")
response.setHeader(
"Content-Length", (finalEndPoint - startPoint + 1).toString()
)
response.setHeader(
"Content-Range", "bytes $startPoint-$finalEndPoint/$contentLength"
)
info["Content-Type"]?.get(0)?.let { response.setHeader("Content-Type", it) }
response.setStatusCode(206)
response.start()
// 使用流式响应
var currentStart = startPoint
// 启动生产者协程下载数据
val producerJob = mutableListOf<Job>()
while (currentStart <= finalEndPoint) {
producerJob.clear()
// 创建通道用于接收数据块
for (i in 0 until THREAD_NUM) {
if (currentStart > finalEndPoint) break
val chunkStart = currentStart
val chunkEnd = minOf(currentStart + partSize - 1, finalEndPoint)
producerJob += CoroutineScope(Dispatchers.IO).launch {
// 异步下载数据块
val data = getVideoStream(chunkStart, chunkEnd, url, headers)
//如果是0开始且检测到恶意头那么就把数据截断
if (chunkStart == 0L) {
val offset = detectMaliciousPrefix(data)
channels[i].send(data.copyOfRange(offset, data.size))
} else {
channels[i].send(data)
}
}
currentStart = chunkEnd + 1
}
for ((index, _) in producerJob.withIndex()) {
val data = channels[index].receive()
SpiderDebug.log("Received chunk: ${data.size} bytes")
response.write(data)
}
}
channels.forEach { it.close() }
} catch (e: Exception) {
SpiderDebug.log("proxyAsync error: ${e.message}")
e.printStackTrace()
response.write("proxyAsync error: ${e.message}")
} finally {
// channels.forEach { it.close() }
}
}
}
fun detectMaliciousPrefix(data: ByteArray): Int {
val buffer = ByteArray(64) // 读取前64字节足够
ByteArrayInputStream(data).use { fis ->
fis.read(buffer)
}
// 检查是否以合法魔数开头
if (isValidVideoHeader(buffer)) {
return 0 // 正常,无恶意前缀
}
// 在后续位置查找合法魔数比如最多跳过前256字节
val searchLimit = minOf(256, data.size)
val searchBuffer = ByteArray(searchLimit)
ByteArrayInputStream(data).use { fis ->
fis.read(searchBuffer)
}
// 尝试从偏移1开始查找合法视频头
for (offset in 1 until searchLimit - 16) {
if (isValidVideoHeader(searchBuffer, offset)) {
SpiderDebug.log("发现合法视频头位于偏移 $offset,疑似被插入恶意前缀!")
return offset
}
}
return 0 // 未找到合法头,可能是损坏或非视频文件
}
// 判断从指定偏移开始是否是合法视频头
fun isValidVideoHeader(data: ByteArray, offset: Int = 0): Boolean {
if (data.size - offset < 8) return false
// MP4 / MOV: ... ftyp
if (offset + 8 <= data.size && data[offset + 4].toInt() == 0x66 && // 'f'
data[offset + 5].toInt() == 0x74 && // 't'
data[offset + 6].toInt() == 0x79 && // 'y'
data[offset + 7].toInt() == 0x70 // 'p'
) {
// 还可进一步校验前4字节是否为合法 size>=8 且合理)
val size =
(data[offset].toLong() and 0xFF shl 24) or (data[offset + 1].toLong() and 0xFF shl 16) or (data[offset + 2].toLong() and 0xFF shl 8) or (data[offset + 3].toLong() and 0xFF)
if (size >= 8 && size <= 0x100000) return true
}
// AVI: RIFF
if (offset + 4 <= data.size && data[offset] == 0x52.toByte() && data[offset + 1] == 0x49.toByte() && data[offset + 2] == 0x46.toByte() && data[offset + 3] == 0x46.toByte()) return true
// MKV
if (offset + 4 <= data.size && data[offset] == 0x1A.toByte() && data[offset + 1] == 0x45.toByte() && data[offset + 2] == 0xDF.toByte() && data[offset + 3] == 0xA3.toByte()) return true
// FLV
if (offset + 4 <= data.size && data[offset] == 0x46.toByte() && data[offset + 1] == 0x4C.toByte() && data[offset + 2] == 0x56.toByte() && data[offset + 3] == 0x01.toByte()) return true
return false
}
private fun queryToMap(query: String?): Map<String, String>? {
if (query == null) {
return null
}
val result: MutableMap<String, String> = HashMap()
for (param in query.split("&".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()) {
val entry = param.split("=".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
if (entry.size > 1) {
result[entry[0]] = entry[1]
} else {
result[entry[0]] = ""
}
}
return result
}
// 辅助函数(需要实现)
private fun parseRangePoint(rangeHeader: String): Pair<Long, Long> {
// 实现范围解析逻辑
val regex = """bytes=(\d+)-(\d*)""".toRegex()
val match = regex.find(rangeHeader) ?: return 0L to -1L
val start = match.groupValues[1].toLong()
val end = match.groupValues[2].takeIf { it.isNotEmpty() }?.toLong() ?: -1L
return start to end
}
private fun getInfo(
url: String?, headers: Map<String, String>
): MutableMap<String, MutableList<String>> {
val newHeaders: MutableMap<String, String> = java.util.HashMap(headers)
newHeaders["Range"] = "bytes=0-" + (1024 * 1024 - 1)
newHeaders["range"] = "bytes=0-" + (1024 * 1024 - 1)
val res = OkHttp.newCall(url, headers)
res.body()?.close()
return res.headers().toMultimap()
}
private fun getContentLength(info: MutableMap<String, MutableList<String>>): Long {
// 实现获取内容长度逻辑
return info["Content-Length"]?.get(0)?.toLong() ?: 0L
}
private fun getVideoStream(
start: Long, end: Long, url: String, headers: Map<String, String>
): ByteArray {
val header = headers.toMutableMap()
// 实现分段下载逻辑
SpiderDebug.log("getVideoStream: $start-$end; ")
header["Range"] = "bytes=$start-$end"
val res = OkHttp.newCall(url, header)
val body = res.body()
return body?.bytes() ?: ByteArray(0)
}
fun buildProxyUrl(url: String, headers: Map<String, String>): String {
urlMap.clear()
headerMap.clear()
val key = Util.MD5(url)
urlMap[key] = url
headerMap[key] = headers
return "http://127.0.0.1:$port/proxy?key=$key"
}
}
/**
package com.github.catvod.utils
import com.github.catvod.crawler.SpiderDebug
import com.github.catvod.net.OkHttp
import com.google.gson.Gson
import io.ktor.http.ContentType
import io.ktor.http.HttpHeaders
import io.ktor.http.HttpStatusCode
import io.ktor.server.application.ApplicationCall
import io.ktor.server.application.call
import io.ktor.server.application.install
import io.ktor.server.engine.embeddedServer
import io.ktor.server.netty.Netty
import io.ktor.server.plugins.callloging.CallLogging
import io.ktor.server.response.respondBytesWriter
import io.ktor.server.response.respondText
import io.ktor.server.routing.get
import io.ktor.server.routing.routing
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.launch
import okhttp3.Response
import java.nio.ByteBuffer
import java.nio.charset.Charset
object KtorServer {
private val THREAD_NUM = Runtime.getRuntime().availableProcessors() * 2
private val infos = mutableMapOf<String, Array<Any>>()
var ser: io.ktor.server.engine.ApplicationEngine? = null
var port = 10010
//每个片1MB
private val partSize = 1024 * 1024 * 1
fun init() {
do {
try {
ser = embeddedServer(Netty, port) {
install(CallLogging)
routing {
get("/") {
call.respondText("ktor running on $port", ContentType.Text.Plain)
}
get("/proxy") {
SpiderDebug.log("代理中: ${call.parameters["url"]}")
val url = Util.base64Decode(call.parameters["url"])
val header: Map<String, String> = Gson().fromJson<Map<String, String>>(
Util.base64Decode(call.parameters["headers"]),
MutableMap::class.java
)
proxyAsync(
url, header, call
)
}
}
}.start(wait = true)
} catch (e: Exception) {
SpiderDebug.log("start server e:" + e.message)
++port
ser?.stop()
}
} while (port < 13000)
SpiderDebug.log("ktorServer start on $port")
}
*/
/** 启动服务器 *//*
fun start() {
CoroutineScope(Dispatchers.IO).launch { init() }
}
*/
/** 停止服务器 *//*
fun stop() {
ser?.stop(1_000, 2_000)
}
*/
/**
* 获取是否分片信息顺带请求一个1MB块
*//*
@Throws(java.lang.Exception::class)
fun getInfo(url: String?, headers: Map<String, String>): Array<Any> {
val newHeaders: MutableMap<String, String> = java.util.HashMap(headers)
newHeaders["Range"] = "bytes=0-" + (1024 * 1024 - 1)
newHeaders["range"] = "bytes=0-" + (1024 * 1024 - 1)
val info = ProxyVideo.proxy(url, newHeaders)
return info
}
private suspend fun proxyAsync(
url: String, headers: Map<String, String>, call: ApplicationCall
) {
val channels = List(THREAD_NUM) { Channel<ByteArray>() }
try {
SpiderDebug.log("--proxyMultiThread: THREAD_NUM: $THREAD_NUM")
var rangeHeader = call.request.headers[HttpHeaders.Range]
//没有range头
if (rangeHeader.isNullOrEmpty()) {
// 处理初始请求
rangeHeader = "bytes=0-"
}
headers.toMutableMap().apply {
put(HttpHeaders.Range, rangeHeader)
}
// 解析范围请求
val (startPoint, endPoint) = parseRangePoint(
rangeHeader
)
SpiderDebug.log("startPoint: $startPoint; endPoint: $endPoint")
val contentLength = getContentLength(url, headers)
SpiderDebug.log("contentLength: $contentLength")
val finalEndPoint = if (endPoint == -1L) contentLength - 1 else endPoint
call.response.headers.apply {
append(HttpHeaders.Connection, "keep-alive")
append(HttpHeaders.ContentLength, (finalEndPoint - startPoint + 1).toString())
append(HttpHeaders.ContentRange, "bytes $startPoint-$finalEndPoint/$contentLength")
}
call.response.status(HttpStatusCode.PartialContent)
// 使用流式响应
call.respondBytesWriter() {
var currentStart = startPoint
// 启动生产者协程下载数据
val producerJob = mutableListOf<Job>()
while (currentStart <= finalEndPoint) {
producerJob.clear()
// 创建通道用于接收数据块
for (i in 0 until THREAD_NUM) {
if (currentStart > finalEndPoint) break
val chunkStart = currentStart
val chunkEnd = minOf(currentStart + partSize - 1, finalEndPoint)
producerJob += CoroutineScope(Dispatchers.IO).launch {
// 异步下载数据块
val data = getVideoStream(chunkStart, chunkEnd, url, headers)
channels[i].send(data)
}
currentStart = chunkEnd + 1
}
for ((index, job) in producerJob.withIndex()) {
val data = channels[index].receive()
SpiderDebug.log("Received chunk: ${data.size} bytes")
writeFully(ByteBuffer.wrap(data))
}
}
}
} catch (e: Exception) {
SpiderDebug.log("error: ${e.message}")
call.respondText("error: ${e.message}", ContentType.Text.Plain)
} finally {
channels.forEach { it.close() }
}
}
// 辅助函数(需要实现)
private fun parseRangePoint(rangeHeader: String): Pair<Long, Long> {
// 实现范围解析逻辑
val regex = """bytes=(\d+)-(\d*)""".toRegex()
val match = regex.find(rangeHeader) ?: return 0L to -1L
val start = match.groupValues[1].toLong()
val end = match.groupValues[2].takeIf { it.isNotEmpty() }?.toLong() ?: -1L
return start to end
}
private fun getContentLength(url: String, headers: Map<String, String>): Long {
// 实现获取内容长度逻辑
val res = OkHttp.newCall(url, headers)
res.body()?.close()
return res.headers(HttpHeaders.ContentLength)[0]?.toLong() ?: 0L
}
private suspend fun getVideoStream(
start: Long, end: Long, url: String, headers: Map<String, String>
): ByteArray {
val header = headers.toMutableMap()
// 实现分段下载逻辑
SpiderDebug.log("getVideoStream: $start-$end; ")
header[HttpHeaders.Range] = "bytes=$start-$end"
val res = OkHttp.newCall(url, header)
val body = res.body()
return body?.bytes() ?: ByteArray(0)
}
private fun downloadRange(
url: String, headerNew: Map<String, String>
): Response? = OkHttp.newCall(url, headerNew)
}
*/

View File

@ -2,22 +2,32 @@ package com.github.catvod.utils;
import android.os.SystemClock; import android.os.SystemClock;
import android.text.TextUtils; import android.text.TextUtils;
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.spider.Proxy; import com.github.catvod.spider.Proxy;
import com.google.gson.Gson; import com.google.gson.Gson;
import okhttp3.Response;
import org.apache.commons.lang3.StringUtils;
import org.json.JSONObject; import org.json.JSONObject;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import okhttp3.Response;
public class ProxyVideo { public class ProxyVideo {
private static final String GO_SERVER = "http://127.0.0.1:7777/"; private static final String GO_SERVER = "http://127.0.0.1:7777/";
//线程数4
private static final int THREAD_NUM = Runtime.getRuntime().availableProcessors() * 2;
private static Map<String, Object[]> infos = new HashMap<>();
public static String buildCommonProxyUrl(String url, Map<String, String> headers) { public static String buildCommonProxyUrl(String url, Map<String, String> headers) {
return Proxy.getUrl() + "?do=proxy&url=" + Util.base64Encode(url.getBytes(Charset.defaultCharset())) + "&header=" + Util.base64Encode((new Gson().toJson(headers)).getBytes(Charset.defaultCharset())); return Proxy.getUrl() + "?do=proxy&url=" + Util.base64Encode(url.getBytes(Charset.defaultCharset())) + "&header=" + Util.base64Encode((new Gson().toJson(headers)).getBytes(Charset.defaultCharset()));
@ -54,10 +64,16 @@ public class ProxyVideo {
SpiderDebug.log(" ++proxy res code:" + response.code()); SpiderDebug.log(" ++proxy res code:" + response.code());
SpiderDebug.log(" ++proxy res header:" + Json.toJson(response.headers())); SpiderDebug.log(" ++proxy res header:" + Json.toJson(response.headers()));
// SpiderDebug.log(" ++proxy res data:" + Json.toJson(response.body())); // SpiderDebug.log(" ++proxy res data:" + Json.toJson(response.body()));
String contentType = response.headers().get("Content-Type");
String contentType = StringUtils.isAllBlank(response.headers().get("Content-Type")) ? response.headers().get("content-type") : response.headers().get("Content-Type");
String contentDisposition = response.headers().get("Content-Disposition"); String contentDisposition = response.headers().get("Content-Disposition");
if (contentDisposition != null) contentType = getMimeType(contentDisposition); if (contentDisposition != null && StringUtils.isAllBlank(contentType)) {
contentType = getMimeType(contentDisposition);
}
Map<String, String> respHeaders = new HashMap<>(); Map<String, String> respHeaders = new HashMap<>();
/* respHeaders.put("Access-Control-Allow-Credentials", "true"); /* respHeaders.put("Access-Control-Allow-Credentials", "true");
respHeaders.put("Access-Control-Allow-Origin", "*");*/ respHeaders.put("Access-Control-Allow-Origin", "*");*/
@ -70,7 +86,26 @@ public class ProxyVideo {
return new Object[]{response.code(), contentType, response.body().byteStream(), respHeaders}; return new Object[]{response.code(), contentType, response.body().byteStream(), respHeaders};
} }
private static String getMimeType(String contentDisposition) {
public static Object[] proxyMultiThread(String url, Map<String, String> headers) {
return DownloadMT.INSTANCE.proxyMultiThread(url, headers);
}
public static Map<String, String> parseRange(String range) {
SpiderDebug.log("parseRange:" + range);
if (StringUtils.isNoneBlank(range)) {
String[] ranges = StringUtils.split(range.replace("bytes=", ""), "-");
String start = ranges[0];
String end = ranges.length > 1 ? ranges[1] : "";
return Map.of("start", start, "end", end);
}
return null;
}
public static String getMimeType(String contentDisposition) {
if (contentDisposition.endsWith(".mp4")) { if (contentDisposition.endsWith(".mp4")) {
return "video/mp4"; return "video/mp4";
} else if (contentDisposition.endsWith(".webm")) { } else if (contentDisposition.endsWith(".webm")) {
@ -101,4 +136,9 @@ public class ProxyVideo {
return null; return null;
} }
} }
/**
* 视频range
*/
} }

View File

@ -23,6 +23,8 @@ import java.util.*;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import static android.net.Uri.encode;
public class Util { public class Util {
public static final String patternAli = "(https:\\/\\/www\\.aliyundrive\\.com\\/s\\/[^\"]+|https:\\/\\/www\\.alipan\\.com\\/s\\/[^\"]+)"; public static final String patternAli = "(https:\\/\\/www\\.aliyundrive\\.com\\/s\\/[^\"]+|https:\\/\\/www\\.alipan\\.com\\/s\\/[^\"]+)";
public static final String patternQuark = "(https:\\/\\/pan\\.quark\\.cn\\/s\\/[^\"]+)"; public static final String patternQuark = "(https:\\/\\/pan\\.quark\\.cn\\/s\\/[^\"]+)";
@ -30,6 +32,8 @@ public class Util {
public static final Pattern RULE = Pattern.compile("http((?!http).){12,}?\\.(m3u8|mp4|mkv|flv|mp3|m4a|aac)\\?.*|http((?!http).){12,}\\.(m3u8|mp4|mkv|flv|mp3|m4a|aac)|http((?!http).)*?video/tos*"); public static final Pattern RULE = Pattern.compile("http((?!http).){12,}?\\.(m3u8|mp4|mkv|flv|mp3|m4a|aac)\\?.*|http((?!http).){12,}\\.(m3u8|mp4|mkv|flv|mp3|m4a|aac)|http((?!http).)*?video/tos*");
public static final Pattern THUNDER = Pattern.compile("(magnet|thunder|ed2k):.*"); public static final Pattern THUNDER = Pattern.compile("(magnet|thunder|ed2k):.*");
public static final String CHROME = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36"; public static final String CHROME = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36";
public static final String MOBILE = "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Mobile Safari/537.36 Edg/142.0.0.0";
public static final String ACCEPT = "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7"; public static final String ACCEPT = "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7";
public static final List<String> MEDIA = Arrays.asList("mp4", "mkv", "wmv", "flv", "avi", "iso", "mpg", "ts", "mp3", "aac", "flac", "m4a", "ape", "ogg"); public static final List<String> MEDIA = Arrays.asList("mp4", "mkv", "wmv", "flv", "avi", "iso", "mpg", "ts", "mp3", "aac", "flac", "m4a", "ape", "ogg");
public static final List<String> SUB = Arrays.asList("srt", "ass", "ssa", "vtt"); public static final List<String> SUB = Arrays.asList("srt", "ass", "ssa", "vtt");
@ -257,6 +261,12 @@ public class Util {
return ""; return "";
} }
} }
public static String getStrByRegex(Pattern pattern, String str) {
Matcher matcher = pattern.matcher(str);
if (matcher.find()) return matcher.group(1).trim();
return "";
}
public static String base64Decode(String s) { public static String base64Decode(String s) {
return new String(android.util.Base64.decode(s, Base64.NO_WRAP), Charset.defaultCharset()); return new String(android.util.Base64.decode(s, Base64.NO_WRAP), Charset.defaultCharset());

View File

@ -0,0 +1,229 @@
/*
package com.github.catvod.utils
import com.github.catvod.crawler.SpiderDebug
import com.github.catvod.net.OkHttp
import com.google.gson.Gson
import io.ktor.http.ContentType
import io.ktor.http.HttpHeaders
import io.ktor.http.HttpStatusCode
import io.ktor.server.application.ApplicationCall
import io.ktor.server.application.call
import io.ktor.server.application.install
import io.ktor.server.engine.embeddedServer
import io.ktor.server.netty.Netty
import io.ktor.server.plugins.callloging.CallLogging
import io.ktor.server.response.respondBytesWriter
import io.ktor.server.response.respondText
import io.ktor.server.routing.get
import io.ktor.server.routing.routing
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.launch
import okhttp3.Response
import java.nio.ByteBuffer
import java.nio.charset.Charset
object KtorServer {
private val THREAD_NUM = Runtime.getRuntime().availableProcessors() * 2
private val infos = mutableMapOf<String, Array<Any>>()
var ser: io.ktor.server.engine.ApplicationEngine? = null
var port = 10010
//每个片1MB
private val partSize = 1024 * 1024 * 1
fun init() {
do {
try {
ser = embeddedServer(Netty, port) {
install(CallLogging)
routing {
get("/") {
call.respondText("ktor running on $port", ContentType.Text.Plain)
}
get("/proxy") {
SpiderDebug.log("代理中: ${call.parameters["url"]}")
val url = Util.base64Decode(call.parameters["url"])
val header: Map<String, String> = Gson().fromJson<Map<String, String>>(
Util.base64Decode(call.parameters["headers"]),
MutableMap::class.java
)
proxyAsync(
url, header, call
)
}
}
}.start(wait = true)
} catch (e: Exception) {
SpiderDebug.log("start server e:" + e.message)
++port
ser?.stop()
}
} while (port < 13000)
SpiderDebug.log("ktorServer start on $port")
}
*/
/** 启动服务器 *//*
fun start() {
CoroutineScope(Dispatchers.IO).launch { init() }
}
*/
/** 停止服务器 *//*
fun stop() {
ser?.stop(1_000, 2_000)
}
fun buildProxyUrl(url: String, headers: Map<String, String>): String {
return "http://127.0.0.1:$port/proxy?url=${Util.base64Encode(url.toByteArray(Charset.defaultCharset()))}&headers=${
Util.base64Encode(
Gson().toJson(headers).toByteArray(
Charset.defaultCharset()
)
)
}"
}
*/
/**
* 获取是否分片信息顺带请求一个1MB块
*//*
@Throws(java.lang.Exception::class)
fun getInfo(url: String?, headers: Map<String, String>): Array<Any> {
val newHeaders: MutableMap<String, String> = java.util.HashMap(headers)
newHeaders["Range"] = "bytes=0-" + (1024 * 1024 - 1)
newHeaders["range"] = "bytes=0-" + (1024 * 1024 - 1)
val info = ProxyVideo.proxy(url, newHeaders)
return info
}
private suspend fun proxyAsync(
url: String, headers: Map<String, String>, call: ApplicationCall
) {
val channels = List(THREAD_NUM) { Channel<ByteArray>() }
try {
SpiderDebug.log("--proxyMultiThread: THREAD_NUM: $THREAD_NUM")
var rangeHeader = call.request.headers[HttpHeaders.Range]
//没有range头
if (rangeHeader.isNullOrEmpty()) {
// 处理初始请求
rangeHeader = "bytes=0-"
}
headers.toMutableMap().apply {
put(HttpHeaders.Range, rangeHeader)
}
// 解析范围请求
val (startPoint, endPoint) = parseRangePoint(
rangeHeader
)
SpiderDebug.log("startPoint: $startPoint; endPoint: $endPoint")
val contentLength = getContentLength(url, headers)
SpiderDebug.log("contentLength: $contentLength")
val finalEndPoint = if (endPoint == -1L) contentLength - 1 else endPoint
call.response.headers.apply {
append(HttpHeaders.Connection, "keep-alive")
append(HttpHeaders.ContentLength, (finalEndPoint - startPoint + 1).toString())
append(HttpHeaders.ContentRange, "bytes $startPoint-$finalEndPoint/$contentLength")
}
call.response.status(HttpStatusCode.PartialContent)
// 使用流式响应
call.respondBytesWriter() {
var currentStart = startPoint
// 启动生产者协程下载数据
val producerJob = mutableListOf<Job>()
while (currentStart <= finalEndPoint) {
producerJob.clear()
// 创建通道用于接收数据块
for (i in 0 until THREAD_NUM) {
if (currentStart > finalEndPoint) break
val chunkStart = currentStart
val chunkEnd = minOf(currentStart + partSize - 1, finalEndPoint)
producerJob += CoroutineScope(Dispatchers.IO).launch {
// 异步下载数据块
val data = getVideoStream(chunkStart, chunkEnd, url, headers)
channels[i].send(data)
}
currentStart = chunkEnd + 1
}
for ((index, job) in producerJob.withIndex()) {
val data = channels[index].receive()
SpiderDebug.log("Received chunk: ${data.size} bytes")
writeFully(ByteBuffer.wrap(data))
}
}
}
} catch (e: Exception) {
SpiderDebug.log("error: ${e.message}")
call.respondText("error: ${e.message}", ContentType.Text.Plain)
} finally {
channels.forEach { it.close() }
}
}
// 辅助函数(需要实现)
private fun parseRangePoint(rangeHeader: String): Pair<Long, Long> {
// 实现范围解析逻辑
val regex = """bytes=(\d+)-(\d*)""".toRegex()
val match = regex.find(rangeHeader) ?: return 0L to -1L
val start = match.groupValues[1].toLong()
val end = match.groupValues[2].takeIf { it.isNotEmpty() }?.toLong() ?: -1L
return start to end
}
private fun getContentLength(url: String, headers: Map<String, String>): Long {
// 实现获取内容长度逻辑
val res = OkHttp.newCall(url, headers)
res.body()?.close()
return res.headers(HttpHeaders.ContentLength)[0]?.toLong() ?: 0L
}
private suspend fun getVideoStream(
start: Long, end: Long, url: String, headers: Map<String, String>
): ByteArray {
val header = headers.toMutableMap()
// 实现分段下载逻辑
SpiderDebug.log("getVideoStream: $start-$end; ")
header[HttpHeaders.Range] = "bytes=$start-$end"
val res = OkHttp.newCall(url, header)
val body = res.body()
return body?.bytes() ?: ByteArray(0)
}
private fun downloadRange(
url: String, headerNew: Map<String, String>
): Response? = OkHttp.newCall(url, headerNew)
}
*/

View File

@ -0,0 +1,64 @@
import android.app.Application;
import com.github.catvod.spider.BaiDuPan;
import com.github.catvod.spider.Init;
import com.github.catvod.utils.Json;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import org.junit.Assert;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.util.ArrayList;
import java.util.Arrays;
@RunWith(RobolectricTestRunner.class)
public class BaiDuPanTest {
private Application mockContext;
private BaiDuPan spider;
@org.junit.Before
public void setUp() throws Exception {
mockContext = RuntimeEnvironment.application;
Init.init(mockContext);
spider = new BaiDuPan();
// spider.init(mockContext, "b-user-id=89ede34e-0efc-e1dd-c997-f16aaa792d0c; _UP_A4A_11_=wb9661c6dfb642f88f73d8e0c7edd398; b-user-id=89ede34e-0efc-e1dd-c997-f16aaa792d0c; ctoken=wla6p3EUOLyn1FSB8IKp1SEW; grey-id=5583e32b-39df-4bf0-f39f-1adf83f604a2; grey-id.sig=p8ReBIMG2BeZu1sYvsuOAZxYbx-MVrsfKEiCv87MsTM; isQuark=true; isQuark.sig=hUgqObykqFom5Y09bll94T1sS9abT1X-4Df_lzgl8nM; _UP_F7E_8D_=ZkyvVHnrBLp1A1NFJIjWi0PwKLOVbxJPcg0RzQPI6KmBtV6ZMgPh38l93pgubgHDQqhaZ2Sfc0qv%2BRantbfg1mWGAUpRMP4RqXP78Wvu%2FCfvkWWGc5NhCTV71tGOIGgDBR3%2Bu6%2Fjj44KlE5biSNDOWW7Bigcz27lvOTidzNw8s%2FWtKAIxWbnCzZn4%2FJMBUub1SIMcW89g57k4mfPmDlCgpZKzxwl6beSfdtZ4RUWXmZOn5v5NkxVKhU4wR0Pq7NklczEGdRq2nIAcu7v22Uw2o%2FxMY0xBdeC9Korm5%2FNHnxl6K%2Bd6FXSoT9a3XIMQO359auZPiZWzrNlZe%2BqnOahXcx7KAhQIRqSOapSmL4ygJor4r5isJhRuDoXy7vJAVuH%2FRDtEJJ8rZTq0BdC23Bz%2B0MrsdgbK%2BiW; _UP_D_=pc; __wpkreporterwid_=3d3f74a7-99b7-4916-3f78-911fc2eb9d87; tfstk=fIoZNxjnbhKwPOu0TWZ4LsaRqirTcudSSmNbnxD0C5VgClMm8xMyB-GsnSu4tjpOflAOmSD-9PNiGl120XrgkVNb1SrqHbJBN3tSBAEYoQOWVUUg9qZ8n1bGGkD3CqGYINKSBABhjnXgp3_Vywz6gSc0Syj3BWf0mr2DLW24eZfiiovEKWefj1q0swq3E82iNEMinMy7SLrcpA4Fh3z_ZAViCfih3PbtdW5N_DuU77AaTijmYRkL2Wq54ENoy5a7ZXxCbok33XzS7QSZgxD-oyoVsdGotql0p2dVu7umC4nLStbiLmParc4FELHrI-c0u2dPVRrs8zoZWKCnIbNZrlHfUCMUz2z8KyXVSlgSFmUojh58OzeqTzgwaGll4YCYKwctDV5coP2LL79eKHxpNTXHmre1kZU32JPWCR_AkP2LL79eLZQY-WeUNdw1.; __pus=2051c82285199d8be553be41dd5a2100AAQ+mmv35G4FDDZ5x+3Mhe2OMbNgweQ1ODbW8zDt9YuP1LQVqHUuAAz9KWLsPjpNtim0AVGHusN4MCosTmbq/khM; __kp=e6604120-6051-11ef-bfe4-c31b6cdd0766; __kps=AATcZArVgS76EPn0FMaV4HEj; __ktd=sii/iz4ePzEaoVirXul7QQ==; __uid=AATcZArVgS76EPn0FMaV4HEj; __itrace_wid=5829b95d-dac1-48d3-bfd5-f60cd9462786; __puus=7da0b96cb710fa1b376934485f977e05AATp/q8/QupT7IiBR1GWqZhxlIRT677smMvoHlLxQA0Lk6CkP0YJBOTl+p9DZgzlMz6w4hPXPgWsokukk8PW7ZfhFfPmv8tKMgLpCGLW+tk57luhNghmSdTeVPkAF59STtyCPBEtiNzNAd/zZJ6qILJDi5ywEBAAAg+gOyWHoLHNUR+QxeHRuQa8g5WWA95J8jebIlrr8rCvI1vjTbtiYktT");
spider.init(mockContext, "BIDUPSID=D122360716A06862B6689D4FE32053A1; PSTM=1756869077; MAWEBCUID=web_KAGxElHEQyrFImqrzARlkIssFfvhfCLGaXDsxmWKbhxsfEsvhs; PANWEB=1; H_WISE_SIDS_BFESS=110085_656765_660924_665265_666660_667682_662823_668348_668761_668756_669314_667565_669628_669693_669848_669685_669681_670119_670114_670116_670307_669051_670308_670357_670598_669700_670824_669791_669664_670557_667838_669418_663788; MCITY=-276%3A; csrfToken=6tn2uwGoOUgn87ZBElHEGjCD; newlogin=1; XFI=627d86b0-a655-11f0-b1eb-0f9d37032cfb; XFCS=B8C411ADE99EE9C3B29FDF31558D538BB636F205DF4F52FE2E57F1312E01079F; XFT=yvSTf+EcMsgYHnGyS774OLqljjYcjil0ZkCgy1g0Aso=; Hm_lvt_182d6d59474cf78db37e0b2248640ea5=1760154644; BDORZ=B490B5EBF6F3CD402E515D22BCDA1598; Hm_lvt_fa0277816200010a74ab7d2895df481b=1760339732; Hm_lpvt_fa0277816200010a74ab7d2895df481b=1760344537; BAIDUID=BFD9E2570D797C8F01BC34C196D7E6B7:FG=1; H_PS_PSSID=60279_63143_64005_64983_65126_65194_65248_65272_65368_65420_65450_65510_65538_65543_65618_65599_65633_65656_65674_65667_65653_65706_65713; BAIDUID_BFESS=BFD9E2570D797C8F01BC34C196D7E6B7:FG=1; delPer=0; PSINO=5; H_WISE_SIDS=60279_63143_64005_64983_65126_65194_65248_65272_65368_65420_65450_65510_65538_65543_65618_65599_65633_65656_65674_65667_65653_65706_65713; BA_HECTOR=ak24050g8ha5ah018l0g2g802ke50i1ker7km25; ZFY=LJHc036:AkCyc1OPqFkXugSCgXnfaMIH:AjDQUOfkXtP4:C; BDCLND=vGxYxxMPQqznqvbjTPyD6CQEOejzkIZgEuuv9D5ctzM%3D; ndut_fmt=C6428C500AB0E62AC4B1CBB9671EF7803530C20D2457081012790E5079FD5D8E; ab_sr=1.0.1_MjFjNTFiYTgwYzAxOWFlOWJmYTRmYjUwYmQwNmJkYTg4NTkxYzA4YTk1YTU0MWIxMDQ1YjgzN2QzZGU4Y2MxMzRlNGQ4M2YxNjJmNzliNWFkMjg0N2U4ZWUzOGRiMzljNzM0MmJmMjc2YzQwYmJhYzdmMWU2NThkYmQ4MjRlODRlZDA0Mjc2ZWE3YmQxNGZiZGE3Yzk1MDU1Njk3NmVlOQ==; ppfuid=FOCoIC3q5fKa8fgJnwzbE67EJ49BGJeplOzf+4l4EOvDuu2RXBRv6R3A1AZMa49I27C0gDDLrJyxcIIeAeEhD8JYsoLTpBiaCXhLqvzbzmvy3SeAW17tKgNq/Xx+RgOdb8TWCFe62MVrDTY6lMf2GrfqL8c87KLF2qFER3obJGmVXQmqM6seEAgB/LlOs0+zGEimjy3MrXEpSuItnI4KDwxAMUTOS16BfoXMcd2lpC/ZmOvCi7lsE0/UM0w4HSMVhh7EfifsXEYHtGj52zkQan6UDL686lBL6BHUyL9m3lfGgLbz7OSojK1zRbqBESR5Pdk2R9IA3lxxOVzA+Iw1TWLSgWjlFVG9Xmh1+20oPSbrzvDjYtVPmZ+9/6evcXmhcO1Y58MgLozKnaQIaLfWRFwa8A3ZyTRp/cDxRMhYc97xUSUZS0ReZYJMPG6nCsxNJlhI2UyeJA6QroZFMelR7tnTNS/pLMWceus0e757/UMPmrThfasmhDJrMFcBfoSrAAv3LCf1Y7/fHL3PTSf9vid/u2VLX4h1nBtx8EF07eCMhWVv+2qjbPV7ZhXk3reaWRFEeso3s/Kc9n/UXtUfNU1sHiCdbrCW5yYsuSM9SPGDZsl7FhTAKw7qIu38vFZiq+DRc8Vbf7jOiN9xPe0lOdZHUhGHZ82rL5jTCsILwcRVCndrarbwmu7G154MpYiKmTXZkqV7Alo4QZzicdyMbWvwvmR2/m//YVTM8qeZWgDSHjDmtehgLWM45zARbPujeqU0T92Gmgs89l2htrSKITeYE0TpfIvjPXsgyQghyP8U3sViHT1z07gbfu1XO5QQ/upk1AkHGkWrkbMWm+rpwpdImdyxYIjA1uSy2hfTFv/d3cnXH4nh+maaicAPllDgrppZTr0lDf2Vsiy73L8egP9ck5gsaaSE4obz9V1JGvyp8lNw+IyCN2Gou0efGgcYWqtuH+3KMtXW4uAv+XUaBDreXqEwrDxmyUrecavkqQ9rGRChHnhPuJeIKACPXiVuli9ItRLEkdb1mLxNHAk3uJy88YX/Rf/sKUjR12zxRTDxxJNDJS+Dlsbqu3n4I65ujli/3rQ8Zk1MjmTOsz9+kTqOM4upsnQ6IWq/zeZTItMCgHpQhuhr4ip73honuzoJgge1cqWBFYvpabAPTOERTOP1kmx5SXPARX5uxyJzAiNILBC8zh7fGfNXOWV37O9gEbYklWGnWpu55tg8Y8GaT7BFVDtu1KaJzjx51nTN1+xVI8c7otOFm3py1Y+wrt2CfI5v5JSd2kRNZE7s6bQrA5yMI31SfUDgxDrsd6lPtUU=; BDUSS=YyTmxnRVJQN2FOeXpOZU5TazhHdX56a1h3NnlsTmVaUlFqTEFKSm43aTNPaFZwSVFBQUFBJCQAAAAAAAAAAAEAAAB0wRMxMTIzNDWwtMqxtPLL4wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALet7Wi3re1odF; BDUSS_BFESS=YyTmxnRVJQN2FOeXpOZU5TazhHdX56a1h3NnlsTmVaUlFqTEFKSm43aTNPaFZwSVFBQUFBJCQAAAAAAAAAAAEAAAB0wRMxMTIzNDWwtMqxtPLL4wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALet7Wi3re1odF; STOKEN=7970ecc47cebf55b8bbd63fee5e5e4b612753bceae56d51073b1b69bab616df6; PANPSC=17920541514426231310%3Au9Rut0jYI4omsQqGlPk7plcS2d9ns3O5C61tf8CKQkigT9ngiMB1eA3MPJHnDhkC6WRWG5ljYM%2BmSRyxE8aegpNZSd%2B1SXYsBm6rqQ5QdYLMOw0WWU7tUSqLe%2F%2Bg%2FDWP4i2i1JdCg%2BQ3uGXWluwqDsVi7NXtJ9t339k3FioH%2FAqA1gqZJvix0rmqJrAOClKcX0r3JWTW68Ah7IX89yGaj0Eyueu6wBwEDZC8PXQ9dy48%2FX4Y2hjenzCvC2qz1EInzo%2F1llKJ4P7gQiGlDEks0p8M%2Flyi7z69BCKhGHn%2Fawh9ff8YI35KVRVksnPVbyr%2FTaFwbj9JM%2FCSsVZaqyA8TA%3D%3D; Hm_lpvt_182d6d59474cf78db37e0b2248640ea5=1760406982");
}
@org.junit.Test
public void init() throws Exception {
spider.init(mockContext, "BIDUPSID=D122360716A06862B6689D4FE32053A1; PSTM=1756869077; MAWEBCUID=web_KAGxElHEQyrFImqrzARlkIssFfvhfCLGaXDsxmWKbhxsfEsvhs; PANWEB=1; H_WISE_SIDS_BFESS=110085_656765_660924_665265_666660_667682_662823_668348_668761_668756_669314_667565_669628_669693_669848_669685_669681_670119_670114_670116_670307_669051_670308_670357_670598_669700_670824_669791_669664_670557_667838_669418_663788; MCITY=-276%3A; csrfToken=6tn2uwGoOUgn87ZBElHEGjCD; newlogin=1; XFI=627d86b0-a655-11f0-b1eb-0f9d37032cfb; XFCS=B8C411ADE99EE9C3B29FDF31558D538BB636F205DF4F52FE2E57F1312E01079F; XFT=yvSTf+EcMsgYHnGyS774OLqljjYcjil0ZkCgy1g0Aso=; Hm_lvt_182d6d59474cf78db37e0b2248640ea5=1760154644; BDORZ=B490B5EBF6F3CD402E515D22BCDA1598; Hm_lvt_fa0277816200010a74ab7d2895df481b=1760339732; Hm_lpvt_fa0277816200010a74ab7d2895df481b=1760344537; BAIDUID=BFD9E2570D797C8F01BC34C196D7E6B7:FG=1; H_PS_PSSID=60279_63143_64005_64983_65126_65194_65248_65272_65368_65420_65450_65510_65538_65543_65618_65599_65633_65656_65674_65667_65653_65706_65713; BAIDUID_BFESS=BFD9E2570D797C8F01BC34C196D7E6B7:FG=1; delPer=0; PSINO=5; H_WISE_SIDS=60279_63143_64005_64983_65126_65194_65248_65272_65368_65420_65450_65510_65538_65543_65618_65599_65633_65656_65674_65667_65653_65706_65713; BA_HECTOR=ak24050g8ha5ah018l0g2g802ke50i1ker7km25; ZFY=LJHc036:AkCyc1OPqFkXugSCgXnfaMIH:AjDQUOfkXtP4:C; BDCLND=vGxYxxMPQqznqvbjTPyD6CQEOejzkIZgEuuv9D5ctzM%3D; ndut_fmt=C6428C500AB0E62AC4B1CBB9671EF7803530C20D2457081012790E5079FD5D8E; ab_sr=1.0.1_MjFjNTFiYTgwYzAxOWFlOWJmYTRmYjUwYmQwNmJkYTg4NTkxYzA4YTk1YTU0MWIxMDQ1YjgzN2QzZGU4Y2MxMzRlNGQ4M2YxNjJmNzliNWFkMjg0N2U4ZWUzOGRiMzljNzM0MmJmMjc2YzQwYmJhYzdmMWU2NThkYmQ4MjRlODRlZDA0Mjc2ZWE3YmQxNGZiZGE3Yzk1MDU1Njk3NmVlOQ==; ppfuid=FOCoIC3q5fKa8fgJnwzbE67EJ49BGJeplOzf+4l4EOvDuu2RXBRv6R3A1AZMa49I27C0gDDLrJyxcIIeAeEhD8JYsoLTpBiaCXhLqvzbzmvy3SeAW17tKgNq/Xx+RgOdb8TWCFe62MVrDTY6lMf2GrfqL8c87KLF2qFER3obJGmVXQmqM6seEAgB/LlOs0+zGEimjy3MrXEpSuItnI4KDwxAMUTOS16BfoXMcd2lpC/ZmOvCi7lsE0/UM0w4HSMVhh7EfifsXEYHtGj52zkQan6UDL686lBL6BHUyL9m3lfGgLbz7OSojK1zRbqBESR5Pdk2R9IA3lxxOVzA+Iw1TWLSgWjlFVG9Xmh1+20oPSbrzvDjYtVPmZ+9/6evcXmhcO1Y58MgLozKnaQIaLfWRFwa8A3ZyTRp/cDxRMhYc97xUSUZS0ReZYJMPG6nCsxNJlhI2UyeJA6QroZFMelR7tnTNS/pLMWceus0e757/UMPmrThfasmhDJrMFcBfoSrAAv3LCf1Y7/fHL3PTSf9vid/u2VLX4h1nBtx8EF07eCMhWVv+2qjbPV7ZhXk3reaWRFEeso3s/Kc9n/UXtUfNU1sHiCdbrCW5yYsuSM9SPGDZsl7FhTAKw7qIu38vFZiq+DRc8Vbf7jOiN9xPe0lOdZHUhGHZ82rL5jTCsILwcRVCndrarbwmu7G154MpYiKmTXZkqV7Alo4QZzicdyMbWvwvmR2/m//YVTM8qeZWgDSHjDmtehgLWM45zARbPujeqU0T92Gmgs89l2htrSKITeYE0TpfIvjPXsgyQghyP8U3sViHT1z07gbfu1XO5QQ/upk1AkHGkWrkbMWm+rpwpdImdyxYIjA1uSy2hfTFv/d3cnXH4nh+maaicAPllDgrppZTr0lDf2Vsiy73L8egP9ck5gsaaSE4obz9V1JGvyp8lNw+IyCN2Gou0efGgcYWqtuH+3KMtXW4uAv+XUaBDreXqEwrDxmyUrecavkqQ9rGRChHnhPuJeIKACPXiVuli9ItRLEkdb1mLxNHAk3uJy88YX/Rf/sKUjR12zxRTDxxJNDJS+Dlsbqu3n4I65ujli/3rQ8Zk1MjmTOsz9+kTqOM4upsnQ6IWq/zeZTItMCgHpQhuhr4ip73honuzoJgge1cqWBFYvpabAPTOERTOP1kmx5SXPARX5uxyJzAiNILBC8zh7fGfNXOWV37O9gEbYklWGnWpu55tg8Y8GaT7BFVDtu1KaJzjx51nTN1+xVI8c7otOFm3py1Y+wrt2CfI5v5JSd2kRNZE7s6bQrA5yMI31SfUDgxDrsd6lPtUU=; BDUSS=YyTmxnRVJQN2FOeXpOZU5TazhHdX56a1h3NnlsTmVaUlFqTEFKSm43aTNPaFZwSVFBQUFBJCQAAAAAAAAAAAEAAAB0wRMxMTIzNDWwtMqxtPLL4wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALet7Wi3re1odF; BDUSS_BFESS=YyTmxnRVJQN2FOeXpOZU5TazhHdX56a1h3NnlsTmVaUlFqTEFKSm43aTNPaFZwSVFBQUFBJCQAAAAAAAAAAAEAAAB0wRMxMTIzNDWwtMqxtPLL4wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALet7Wi3re1odF; STOKEN=7970ecc47cebf55b8bbd63fee5e5e4b612753bceae56d51073b1b69bab616df6; PANPSC=17920541514426231310%3Au9Rut0jYI4omsQqGlPk7plcS2d9ns3O5C61tf8CKQkigT9ngiMB1eA3MPJHnDhkC6WRWG5ljYM%2BmSRyxE8aegpNZSd%2B1SXYsBm6rqQ5QdYLMOw0WWU7tUSqLe%2F%2Bg%2FDWP4i2i1JdCg%2BQ3uGXWluwqDsVi7NXtJ9t339k3FioH%2FAqA1gqZJvix0rmqJrAOClKcX0r3JWTW68Ah7IX89yGaj0Eyueu6wBwEDZC8PXQ9dy48%2FX4Y2hjenzCvC2qz1EInzo%2F1llKJ4P7gQiGlDEks0p8M%2Flyi7z69BCKhGHn%2Fawh9ff8YI35KVRVksnPVbyr%2FTaFwbj9JM%2FCSsVZaqyA8TA%3D%3D; Hm_lpvt_182d6d59474cf78db37e0b2248640ea5=1760406982");
//Assert.assertFalse(map.getAsJsonArray("list").isEmpty());
}
@org.junit.Test
public void detailContent() throws Exception {
String content = spider.detailContent(Arrays.asList("https://pan.baidu.com/s/1Ov0S6S7rqnyW_S3AondhmQ?pwd=8888"));
System.out.println("detailContent--" + content);
JsonObject map = Json.safeObject(content);
Gson gson = new GsonBuilder().setPrettyPrinting().create();
System.out.println("detailContent--" + gson.toJson(map));
Assert.assertFalse(map.getAsJsonArray("list").isEmpty());
}
@org.junit.Test
public void playerContent() throws Exception {
String content = spider.playerContent("BD原画", "eyJ1ayI6IjExMDMyNzkxMjIzNDEiLCJzaGFyZWlkIjoiMjk1NzE4ODcyOTgiLCJmaWQiOjMxOTM5NTUxMTQyOTU4MCwicmFuZHNrIjoidkd4WXh4TVBRcXpucXZialRQeUQ2Q1FFT2VqemtJWmdFdXV2OUQ1Y3R6TSUzRCIsInBuYW1lIjoiRHJhZ29uIEJhbGwgREFJTUEuUzAxRTAxLjIwMjQuMTA4MHAuQ1IuV0VCLURMLngyNjQuQUFDLm1rdiIsInF0eXBlIjoib3JpZ2luYWwifQ==", new ArrayList<>());
// String content = spider.playerContent("BD转码", "eyJ1ayI6IjExMDMyNzkxMjIzNDEiLCJmaWQiOjMxOTE2ODY2NDUxMzY1Nywic2hhcmVpZCI6IjI5NTcxODg3Mjk4Iiwic3VybCI6IjFPdjBTNlM3cnFueVdfUzNBb25kaG1RIiwicG5hbWUiOiJEcmFnb24gQmFsbCBEQUlNQS5TMDFFMjAuMjAyNC4xMDgwcC5DUi5XRUItREwueDI2NC5BQUMubWt2IiwicXR5cGUiOiJwcmV2aWV3In0=", new ArrayList<>());
System.out.println("playerContent--" + content);
JsonObject map = Json.safeObject(content);
Gson gson = new GsonBuilder().setPrettyPrinting().create();
System.out.println("playerContent--" + gson.toJson(map));
Assert.assertFalse(map.getAsJsonPrimitive("url").getAsString().isEmpty());
while (true) {
Thread.sleep(1000);
}
}
}

View File

@ -0,0 +1,103 @@
import android.app.Application
import com.github.catvod.spider.DiDa
import com.github.catvod.spider.Init
import com.github.catvod.utils.Json
import com.google.gson.GsonBuilder
import org.junit.Assert
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.RuntimeEnvironment
@RunWith(RobolectricTestRunner::class)
class DiDaTest {
// @Mock
private var mockContext: Application? = null
private var spider: DiDa? = null
@Before
@Throws(Exception::class)
fun setUp() {
mockContext = RuntimeEnvironment.application
Init.init(mockContext)
spider = DiDa()
spider!!.init(mockContext, "")
}
@Test
@Throws(Exception::class)
fun homeContent() {
val content = spider!!.homeContent(true)
val map = Json.safeObject(content)
val gson = GsonBuilder().setPrettyPrinting().create()
println("homeContent--" + gson.toJson(map))
//Assert.assertFalse(map.getAsJsonArray("list").isEmpty());
}
@Test
@Throws(Exception::class)
fun homeVideoContent() {
val content = spider!!.homeVideoContent()
val map = Json.safeObject(content)
val gson = GsonBuilder().setPrettyPrinting().create()
println("homeVideoContent--" + gson.toJson(map))
//Assert.assertFalse(map.getAsJsonArray("list").isEmpty());
}
@Test
@Throws(Exception::class)
fun categoryContent() {
val content = spider!!.categoryContent("/type/1.html", "2", true, null)
val map = Json.safeObject(content)
val gson = GsonBuilder().setPrettyPrinting().create()
println("categoryContent--" + gson.toJson(map))
Assert.assertFalse(map.getAsJsonArray("list").isEmpty())
}
@Test
@Throws(Exception::class)
fun detailContent() {
val content = spider!!.detailContent(mutableListOf<String?>("/detail/1370.html"))
val map = Json.safeObject(content)
val gson = GsonBuilder().setPrettyPrinting().create()
println("detailContent--" + gson.toJson(map))
Assert.assertFalse(map.getAsJsonArray("list").isEmpty())
}
@Test
@Throws(Exception::class)
fun playerContent() {
val froms ="BD3$$\$超清F$$\$超清A$$\$超清B$$\$夸克网盘$$\$百度网盘"
val urls ="HD1080P英语$/play/1370-1-1.html$$$1080P$/play/1370-6-1.html$$\$HD$/play/1370-3-1.html$$$1080P$/play/1370-4-1.html$$$1080P$/play/1370-2-1.html$$\$合集$/play/1370-5-1.html"
for (i in urls.split("\\$\\$\\$".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray().indices) {
val content = spider!!.playerContent(
froms.split("\\$\\$\\$".toRegex()).dropLastWhile { it.isEmpty() }
.toTypedArray()[i],
urls.split("\\$\\$\\$".toRegex()).dropLastWhile { it.isEmpty() }
.toTypedArray()[i].split("\\$".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()[1],
ArrayList<String?>()
)
val map = Json.safeObject(content)
val gson = GsonBuilder().setPrettyPrinting().create()
println("playerContent--" + gson.toJson(map))
Assert.assertFalse(map.getAsJsonPrimitive("url").getAsString().isEmpty())
}
}
@Test
@Throws(Exception::class)
fun searchContent() {
val content = spider!!.searchContent("红海", false)
val map = Json.safeObject(content)
val gson = GsonBuilder().setPrettyPrinting().create()
println("searchContent--" + gson.toJson(map))
Assert.assertFalse(map.getAsJsonArray("list").isEmpty())
}
}

View File

@ -70,7 +70,7 @@ public class LeiJingTest {
@org.junit.Test @org.junit.Test
public void detailContent() throws Exception { public void detailContent() throws Exception {
String content = spider.detailContent(Arrays.asList("thread?topicId=35472")); String content = spider.detailContent(Arrays.asList("thread?topicId=40214"));
System.out.println("detailContent--" + content); System.out.println("detailContent--" + content);
JsonObject map = Json.safeObject(content); JsonObject map = Json.safeObject(content);

View File

@ -0,0 +1,65 @@
import android.app.Application;
import com.github.catvod.spider.Init;
import com.github.catvod.spider.Pan123;
import com.github.catvod.spider.TianYi;
import com.github.catvod.utils.Json;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import org.junit.Assert;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.util.ArrayList;
import java.util.Arrays;
@RunWith(RobolectricTestRunner.class)
public class Pan123Test {
private Application mockContext;
private Pan123 spider;
@org.junit.Before
public void setUp() throws Exception {
mockContext = RuntimeEnvironment.application;
Init.init(mockContext);
spider = new Pan123();
// spider.init(mockContext, "b-user-id=89ede34e-0efc-e1dd-c997-f16aaa792d0c; _UP_A4A_11_=wb9661c6dfb642f88f73d8e0c7edd398; b-user-id=89ede34e-0efc-e1dd-c997-f16aaa792d0c; ctoken=wla6p3EUOLyn1FSB8IKp1SEW; grey-id=5583e32b-39df-4bf0-f39f-1adf83f604a2; grey-id.sig=p8ReBIMG2BeZu1sYvsuOAZxYbx-MVrsfKEiCv87MsTM; isQuark=true; isQuark.sig=hUgqObykqFom5Y09bll94T1sS9abT1X-4Df_lzgl8nM; _UP_F7E_8D_=ZkyvVHnrBLp1A1NFJIjWi0PwKLOVbxJPcg0RzQPI6KmBtV6ZMgPh38l93pgubgHDQqhaZ2Sfc0qv%2BRantbfg1mWGAUpRMP4RqXP78Wvu%2FCfvkWWGc5NhCTV71tGOIGgDBR3%2Bu6%2Fjj44KlE5biSNDOWW7Bigcz27lvOTidzNw8s%2FWtKAIxWbnCzZn4%2FJMBUub1SIMcW89g57k4mfPmDlCgpZKzxwl6beSfdtZ4RUWXmZOn5v5NkxVKhU4wR0Pq7NklczEGdRq2nIAcu7v22Uw2o%2FxMY0xBdeC9Korm5%2FNHnxl6K%2Bd6FXSoT9a3XIMQO359auZPiZWzrNlZe%2BqnOahXcx7KAhQIRqSOapSmL4ygJor4r5isJhRuDoXy7vJAVuH%2FRDtEJJ8rZTq0BdC23Bz%2B0MrsdgbK%2BiW; _UP_D_=pc; __wpkreporterwid_=3d3f74a7-99b7-4916-3f78-911fc2eb9d87; tfstk=fIoZNxjnbhKwPOu0TWZ4LsaRqirTcudSSmNbnxD0C5VgClMm8xMyB-GsnSu4tjpOflAOmSD-9PNiGl120XrgkVNb1SrqHbJBN3tSBAEYoQOWVUUg9qZ8n1bGGkD3CqGYINKSBABhjnXgp3_Vywz6gSc0Syj3BWf0mr2DLW24eZfiiovEKWefj1q0swq3E82iNEMinMy7SLrcpA4Fh3z_ZAViCfih3PbtdW5N_DuU77AaTijmYRkL2Wq54ENoy5a7ZXxCbok33XzS7QSZgxD-oyoVsdGotql0p2dVu7umC4nLStbiLmParc4FELHrI-c0u2dPVRrs8zoZWKCnIbNZrlHfUCMUz2z8KyXVSlgSFmUojh58OzeqTzgwaGll4YCYKwctDV5coP2LL79eKHxpNTXHmre1kZU32JPWCR_AkP2LL79eLZQY-WeUNdw1.; __pus=2051c82285199d8be553be41dd5a2100AAQ+mmv35G4FDDZ5x+3Mhe2OMbNgweQ1ODbW8zDt9YuP1LQVqHUuAAz9KWLsPjpNtim0AVGHusN4MCosTmbq/khM; __kp=e6604120-6051-11ef-bfe4-c31b6cdd0766; __kps=AATcZArVgS76EPn0FMaV4HEj; __ktd=sii/iz4ePzEaoVirXul7QQ==; __uid=AATcZArVgS76EPn0FMaV4HEj; __itrace_wid=5829b95d-dac1-48d3-bfd5-f60cd9462786; __puus=7da0b96cb710fa1b376934485f977e05AATp/q8/QupT7IiBR1GWqZhxlIRT677smMvoHlLxQA0Lk6CkP0YJBOTl+p9DZgzlMz6w4hPXPgWsokukk8PW7ZfhFfPmv8tKMgLpCGLW+tk57luhNghmSdTeVPkAF59STtyCPBEtiNzNAd/zZJ6qILJDi5ywEBAAAg+gOyWHoLHNUR+QxeHRuQa8g5WWA95J8jebIlrr8rCvI1vjTbtiYktT");
// spider.init(mockContext, "{\"open.e.189.cn\":[{\"name\":\"SSON\",\"value\":\"dc466c8192e3109eaea837c1d136c1fd065253ce1c7d3a66ca1520d7d6d6307b10a1fe65c7becac73b95f24a6e681e654ec4f47c39533ebcc48bb78d6d6e63d1bbf3334e6e97eaa7092d34f87bf1209ee35f344871bc5a329eac34ae948d399d4a6b3b28a929c4f353ade0981657e9e0f09ce27cc1c15d8322c6e45a8ebb21eb431509f1dd7dc3a7856b32b0991d654d5ced73dd20b764ca8737600cbe699c37ccf59b3c610893fc42bdc08b477c5d394e290c14d175d1ca0ee9fa61a1a8dcac7007e9219fd0ae6ccd5dc760524213f85b6b8c6166af01a31336dab797d9118010b81a5a3c26e08e\",\"expiresAt\":253402300799999,\"domain\":\"e.189.cn\",\"path\":\"/\",\"secure\":true,\"httpOnly\":true,\"persistent\":true,\"hostOnly\":false},{\"name\":\"GUID\",\"value\":\"525d8874e53e46a7ba3ed8907e9fef1f\",\"expiresAt\":1775176321000,\"domain\":\"e.189.cn\",\"path\":\"/\",\"secure\":false,\"httpOnly\":false,\"persistent\":true,\"hostOnly\":false},{\"name\":\"pageOp\",\"value\":\"336b9ddc820212fa6c9b5a0cfd7bf5b3\",\"expiresAt\":253402300799999,\"domain\":\"e.189.cn\",\"path\":\"/\",\"secure\":false,\"httpOnly\":false,\"persistent\":false,\"hostOnly\":false},{\"name\":\"OPENINFO\",\"value\":\"33c28688ef52ce9e3a9ef87388047efbde5e3e2e4c7ef6ef267632468c7dfaf294ff59fa59d34801\",\"expiresAt\":253402300799999,\"domain\":\"e.189.cn\",\"path\":\"/\",\"secure\":false,\"httpOnly\":true,\"persistent\":false,\"hostOnly\":false},{\"name\":\"GRAYNUMBER\",\"value\":\"319DE3F68C8730862F3BEF66F3D635B7\",\"expiresAt\":1775177653000,\"domain\":\"e.189.cn\",\"path\":\"/\",\"secure\":false,\"httpOnly\":false,\"persistent\":true,\"hostOnly\":false}],\"cloud.189.cn\":[{\"name\":\"JSESSIONID\",\"value\":\"431787526C43DF21B6373E914FE597EC\",\"expiresAt\":253402300799999,\"domain\":\"cloud.189.cn\",\"path\":\"/\",\"secure\":false,\"httpOnly\":true,\"persistent\":false,\"hostOnly\":true},{\"name\":\"COOKIE_LOGIN_USER\",\"value\":\"0C7407F59A6E5896EB6B777056E160DB020BAE67B121B5930CCD4777073744055308F7E8CD03F2FC2399E4823F60ECDD74120CEE4C529017\",\"expiresAt\":253402300799999,\"domain\":\"cloud.189.cn\",\"path\":\"/\",\"secure\":false,\"httpOnly\":true,\"persistent\":false,\"hostOnly\":false}]}");
// Server.get().start();
spider.init(mockContext, "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3NjE1MjEyNzksImlhdCI6MTc2MDkxNjQ3OSwiaWQiOjE4NDc0Mjg3NzAsIm1haWwiOiIiLCJuaWNrbmFtZSI6IjE4ODk2NzgxNjAxIiwic3VwcGVyIjpmYWxzZSwidXNlcm5hbWUiOjE4ODk2NzgxNjAxLCJ2IjowfQ.gVhIfG1t2tUts68eYY-AdUfJoBKBNeG41k3XGYfwCek");
}
@org.junit.Test
public void init() throws Exception {
spider.init(mockContext, "{\"open.e.189.cn\":[{\"name\":\"SSON\",\"value\":\"dc466c8192e3109eaea837c1d136c1fd065253ce1c7d3a66ca1520d7d6d6307b10a1fe65c7becac73b95f24a6e681e654ec4f47c39533ebcc48bb78d6d6e63d1bbf3334e6e97eaa7092d34f87bf1209ee35f344871bc5a329eac34ae948d399d4a6b3b28a929c4f353ade0981657e9e0f09ce27cc1c15d8322c6e45a8ebb21eb431509f1dd7dc3a7856b32b0991d654d5ced73dd20b764ca8737600cbe699c37ccf59b3c610893fc42bdc08b477c5d394e290c14d175d1ca0ee9fa61a1a8dcac7007e9219fd0ae6ccd5dc760524213f85b6b8c6166af01a31336dab797d9118010b81a5a3c26e08e\",\"expiresAt\":253402300799999,\"domain\":\"e.189.cn\",\"path\":\"/\",\"secure\":true,\"httpOnly\":true,\"persistent\":true,\"hostOnly\":false},{\"name\":\"GUID\",\"value\":\"525d8874e53e46a7ba3ed8907e9fef1f\",\"expiresAt\":1775176321000,\"domain\":\"e.189.cn\",\"path\":\"/\",\"secure\":false,\"httpOnly\":false,\"persistent\":true,\"hostOnly\":false},{\"name\":\"pageOp\",\"value\":\"336b9ddc820212fa6c9b5a0cfd7bf5b3\",\"expiresAt\":253402300799999,\"domain\":\"e.189.cn\",\"path\":\"/\",\"secure\":false,\"httpOnly\":false,\"persistent\":false,\"hostOnly\":false},{\"name\":\"OPENINFO\",\"value\":\"33c28688ef52ce9e3a9ef87388047efbde5e3e2e4c7ef6ef267632468c7dfaf294ff59fa59d34801\",\"expiresAt\":253402300799999,\"domain\":\"e.189.cn\",\"path\":\"/\",\"secure\":false,\"httpOnly\":true,\"persistent\":false,\"hostOnly\":false},{\"name\":\"GRAYNUMBER\",\"value\":\"319DE3F68C8730862F3BEF66F3D635B7\",\"expiresAt\":1775177653000,\"domain\":\"e.189.cn\",\"path\":\"/\",\"secure\":false,\"httpOnly\":false,\"persistent\":true,\"hostOnly\":false}],\"cloud.189.cn\":[{\"name\":\"JSESSIONID\",\"value\":\"431787526C43DF21B6373E914FE597EC\",\"expiresAt\":253402300799999,\"domain\":\"cloud.189.cn\",\"path\":\"/\",\"secure\":false,\"httpOnly\":true,\"persistent\":false,\"hostOnly\":true},{\"name\":\"COOKIE_LOGIN_USER\",\"value\":\"0C7407F59A6E5896EB6B777056E160DB020BAE67B121B5930CCD4777073744055308F7E8CD03F2FC2399E4823F60ECDD74120CEE4C529017\",\"expiresAt\":253402300799999,\"domain\":\"cloud.189.cn\",\"path\":\"/\",\"secure\":false,\"httpOnly\":true,\"persistent\":false,\"hostOnly\":false}]}");
//Assert.assertFalse(map.getAsJsonArray("list").isEmpty());
}
@org.junit.Test
public void detailContent() throws Exception {
String content = spider.detailContent(Arrays.asList("https://www.123865.com/s/u9izjv-6hSWv"));
System.out.println("detailContent--" + content);
JsonObject map = Json.safeObject(content);
Gson gson = new GsonBuilder().setPrettyPrinting().create();
System.out.println("detailContent--" + gson.toJson(map));
Assert.assertFalse(map.getAsJsonArray("list").isEmpty());
}
@org.junit.Test
public void playerContent() throws Exception {
String content = spider.playerContent("pan123原画","eyJmaWxlbmFtZSI6IuWRveaCrOS4gOeUny5TMDFFMDEuMjAyNS4yMTYwcC5XRUItREwuSDI2NS5ERFA1LjEubWt2Iiwic2hhcmVLZXkiOiJ1OWl6anYtNmhTV3YiLCJzaGFyZVB3ZCI6IiIsIm5leHQiOi0xLCJmaWxlSWQiOjI3MzQ0MjY3LCJTM0tleUZsYWciOiIxODI3MDg2NzQ5LTAiLCJTaXplIjoxNDExNDM3ODI2LCJFdGFnIjoiZGZmOGZlZWU2N2JkYzRhOTQxMjlhZTJlNTg1YmI0Y2QifQ==",new ArrayList<>());
System.out.println("playerContent--" + content);
JsonObject map = Json.safeObject(content);
Gson gson = new GsonBuilder().setPrettyPrinting().create();
System.out.println("playerContent--" + gson.toJson(map));
Assert.assertFalse(map.getAsJsonPrimitive("url").getAsString().isEmpty());
while(true){
}
}
}

View File

@ -28,7 +28,7 @@ public class QuarkTest {
Init.init(mockContext); Init.init(mockContext);
spider = new Quark(); spider = new Quark();
// spider.init(mockContext, "b-user-id=89ede34e-0efc-e1dd-c997-f16aaa792d0c; _UP_A4A_11_=wb9661c6dfb642f88f73d8e0c7edd398; b-user-id=89ede34e-0efc-e1dd-c997-f16aaa792d0c; ctoken=wla6p3EUOLyn1FSB8IKp1SEW; grey-id=5583e32b-39df-4bf0-f39f-1adf83f604a2; grey-id.sig=p8ReBIMG2BeZu1sYvsuOAZxYbx-MVrsfKEiCv87MsTM; isQuark=true; isQuark.sig=hUgqObykqFom5Y09bll94T1sS9abT1X-4Df_lzgl8nM; _UP_F7E_8D_=ZkyvVHnrBLp1A1NFJIjWi0PwKLOVbxJPcg0RzQPI6KmBtV6ZMgPh38l93pgubgHDQqhaZ2Sfc0qv%2BRantbfg1mWGAUpRMP4RqXP78Wvu%2FCfvkWWGc5NhCTV71tGOIGgDBR3%2Bu6%2Fjj44KlE5biSNDOWW7Bigcz27lvOTidzNw8s%2FWtKAIxWbnCzZn4%2FJMBUub1SIMcW89g57k4mfPmDlCgpZKzxwl6beSfdtZ4RUWXmZOn5v5NkxVKhU4wR0Pq7NklczEGdRq2nIAcu7v22Uw2o%2FxMY0xBdeC9Korm5%2FNHnxl6K%2Bd6FXSoT9a3XIMQO359auZPiZWzrNlZe%2BqnOahXcx7KAhQIRqSOapSmL4ygJor4r5isJhRuDoXy7vJAVuH%2FRDtEJJ8rZTq0BdC23Bz%2B0MrsdgbK%2BiW; _UP_D_=pc; __wpkreporterwid_=3d3f74a7-99b7-4916-3f78-911fc2eb9d87; tfstk=fIoZNxjnbhKwPOu0TWZ4LsaRqirTcudSSmNbnxD0C5VgClMm8xMyB-GsnSu4tjpOflAOmSD-9PNiGl120XrgkVNb1SrqHbJBN3tSBAEYoQOWVUUg9qZ8n1bGGkD3CqGYINKSBABhjnXgp3_Vywz6gSc0Syj3BWf0mr2DLW24eZfiiovEKWefj1q0swq3E82iNEMinMy7SLrcpA4Fh3z_ZAViCfih3PbtdW5N_DuU77AaTijmYRkL2Wq54ENoy5a7ZXxCbok33XzS7QSZgxD-oyoVsdGotql0p2dVu7umC4nLStbiLmParc4FELHrI-c0u2dPVRrs8zoZWKCnIbNZrlHfUCMUz2z8KyXVSlgSFmUojh58OzeqTzgwaGll4YCYKwctDV5coP2LL79eKHxpNTXHmre1kZU32JPWCR_AkP2LL79eLZQY-WeUNdw1.; __pus=2051c82285199d8be553be41dd5a2100AAQ+mmv35G4FDDZ5x+3Mhe2OMbNgweQ1ODbW8zDt9YuP1LQVqHUuAAz9KWLsPjpNtim0AVGHusN4MCosTmbq/khM; __kp=e6604120-6051-11ef-bfe4-c31b6cdd0766; __kps=AATcZArVgS76EPn0FMaV4HEj; __ktd=sii/iz4ePzEaoVirXul7QQ==; __uid=AATcZArVgS76EPn0FMaV4HEj; __itrace_wid=5829b95d-dac1-48d3-bfd5-f60cd9462786; __puus=7da0b96cb710fa1b376934485f977e05AATp/q8/QupT7IiBR1GWqZhxlIRT677smMvoHlLxQA0Lk6CkP0YJBOTl+p9DZgzlMz6w4hPXPgWsokukk8PW7ZfhFfPmv8tKMgLpCGLW+tk57luhNghmSdTeVPkAF59STtyCPBEtiNzNAd/zZJ6qILJDi5ywEBAAAg+gOyWHoLHNUR+QxeHRuQa8g5WWA95J8jebIlrr8rCvI1vjTbtiYktT"); // spider.init(mockContext, "b-user-id=89ede34e-0efc-e1dd-c997-f16aaa792d0c; _UP_A4A_11_=wb9661c6dfb642f88f73d8e0c7edd398; b-user-id=89ede34e-0efc-e1dd-c997-f16aaa792d0c; ctoken=wla6p3EUOLyn1FSB8IKp1SEW; grey-id=5583e32b-39df-4bf0-f39f-1adf83f604a2; grey-id.sig=p8ReBIMG2BeZu1sYvsuOAZxYbx-MVrsfKEiCv87MsTM; isQuark=true; isQuark.sig=hUgqObykqFom5Y09bll94T1sS9abT1X-4Df_lzgl8nM; _UP_F7E_8D_=ZkyvVHnrBLp1A1NFJIjWi0PwKLOVbxJPcg0RzQPI6KmBtV6ZMgPh38l93pgubgHDQqhaZ2Sfc0qv%2BRantbfg1mWGAUpRMP4RqXP78Wvu%2FCfvkWWGc5NhCTV71tGOIGgDBR3%2Bu6%2Fjj44KlE5biSNDOWW7Bigcz27lvOTidzNw8s%2FWtKAIxWbnCzZn4%2FJMBUub1SIMcW89g57k4mfPmDlCgpZKzxwl6beSfdtZ4RUWXmZOn5v5NkxVKhU4wR0Pq7NklczEGdRq2nIAcu7v22Uw2o%2FxMY0xBdeC9Korm5%2FNHnxl6K%2Bd6FXSoT9a3XIMQO359auZPiZWzrNlZe%2BqnOahXcx7KAhQIRqSOapSmL4ygJor4r5isJhRuDoXy7vJAVuH%2FRDtEJJ8rZTq0BdC23Bz%2B0MrsdgbK%2BiW; _UP_D_=pc; __wpkreporterwid_=3d3f74a7-99b7-4916-3f78-911fc2eb9d87; tfstk=fIoZNxjnbhKwPOu0TWZ4LsaRqirTcudSSmNbnxD0C5VgClMm8xMyB-GsnSu4tjpOflAOmSD-9PNiGl120XrgkVNb1SrqHbJBN3tSBAEYoQOWVUUg9qZ8n1bGGkD3CqGYINKSBABhjnXgp3_Vywz6gSc0Syj3BWf0mr2DLW24eZfiiovEKWefj1q0swq3E82iNEMinMy7SLrcpA4Fh3z_ZAViCfih3PbtdW5N_DuU77AaTijmYRkL2Wq54ENoy5a7ZXxCbok33XzS7QSZgxD-oyoVsdGotql0p2dVu7umC4nLStbiLmParc4FELHrI-c0u2dPVRrs8zoZWKCnIbNZrlHfUCMUz2z8KyXVSlgSFmUojh58OzeqTzgwaGll4YCYKwctDV5coP2LL79eKHxpNTXHmre1kZU32JPWCR_AkP2LL79eLZQY-WeUNdw1.; __pus=2051c82285199d8be553be41dd5a2100AAQ+mmv35G4FDDZ5x+3Mhe2OMbNgweQ1ODbW8zDt9YuP1LQVqHUuAAz9KWLsPjpNtim0AVGHusN4MCosTmbq/khM; __kp=e6604120-6051-11ef-bfe4-c31b6cdd0766; __kps=AATcZArVgS76EPn0FMaV4HEj; __ktd=sii/iz4ePzEaoVirXul7QQ==; __uid=AATcZArVgS76EPn0FMaV4HEj; __itrace_wid=5829b95d-dac1-48d3-bfd5-f60cd9462786; __puus=7da0b96cb710fa1b376934485f977e05AATp/q8/QupT7IiBR1GWqZhxlIRT677smMvoHlLxQA0Lk6CkP0YJBOTl+p9DZgzlMz6w4hPXPgWsokukk8PW7ZfhFfPmv8tKMgLpCGLW+tk57luhNghmSdTeVPkAF59STtyCPBEtiNzNAd/zZJ6qILJDi5ywEBAAAg+gOyWHoLHNUR+QxeHRuQa8g5WWA95J8jebIlrr8rCvI1vjTbtiYktT");
spider.init(mockContext, "_UP_A4A_11_=wb9681fbaed6454a8112f31e53b5c0be; __pus=45beefa93e8775c9211487d0c8ddd2b1AASCmV5S7LY0dfX90N3p4wU/G4f/oS0gZK6cpxZMZiDtXt9s7KiSs3tVZOXnIDel69C9KaQ61IQlnLYH2rS4NGjO; __kp=fe663a90-68d5-11ef-8b23-e77b0eaa352c; __kps=AAT32Fob+vq66znO5UHSHAPi; __ktd=39oXE+BT53YlFgUfFVq9kw==; __uid=AAT32Fob+vq66znO5UHSHAPi; xlly_s=1; b-user-id=91d551ad-db9e-f092-2b42-aa4db35b8ed0; isg=BNXVFRgkH_dXwTuJ8PizGl2W5NGP0onkjXrhLld60cybrvegHyMutYcsfLIYrqGc; tfstk=fNlSqGqmrgj57RpyCwL4hfk8bfFCODOw9waKS2CPJ7F-JWib0zWyEJ-IhDn42_P82ZEQ7caRpHf8M9aTxuFEUuzKc2nJry8uwiCY7PzLvzeLHsUUP6Ud9WQoncuOabJuT6NutWKwbCRZz4V39rUtZ-SukyUWT_U8JbtmmvxwbCR2eZFBVhkeml_RcyqYyzEdwENYSyFdv_nLD-UT7gFKvWLbHy4G2aCLJoUYJoEL9WnKkjXR5y97q4TY2O9qutVEyo1soja-eT0zc6CKGxw7XBrf96hbPqfsiZf6LlHg4RrtDI57OqUIcRkWf_iIJVDLhXsJzcnxdDUmnH6Qfv3rIj2RJT3jOuwtw-_9SVg8JDeInh1aP7kbCbMk-i3-buMTZ2764qwshR4YHw68aAuZtRhJNGq4IyibQYt1NcIzH1r_KcBClRfQll8Xl9Xh_6YQJFIaDQy8ozw2lEs-K8U0ll8Xl9X3er477ETf2vf..; __puus=514ad4334da84f912529719d557085b2AASV1aKJKLRXGjvHRfwQJ5gupjOzlxgeAImozKKYdppZduMrKS7Q5+3hUZZ0f6zk7YpAGu7p0GVPYojTpZdhvnamXzCBLryM3ULhlqkw9yR6oVeTr3b1MituYgqfeFM4jHi4ASNiLk22pCNKteAtD6aowAM0K1ZFVc7j7xlpxLEgS1CoNSttupAb56Zf+ruuTkDPsjZPiRW1S4yM/kduA247"); spider.init(mockContext, "_UP_28A_52_=386;_UP_BT_=html5;_UP_F7E_8D_=0z44HdIBxZZTFH3p1NV%2FwWJIkAWBTYaH20RoPCksvMmyhI6XxrMIHoi8gAqVoKf%2Bfw0hw4mmmcFLHpvA%2Fhicy1HUTu2LBlCP6GF%2FnM%2Bm0IJoj1BQdak3tm1o3OeN1OV9dQAEQ0UDfWTXDik4ZZxmO5Iwvj6IsFkb5GPrrCl5M87ivs0EP%2FjAQTQimMgEdat62Byd22%2BZGM703ymU3s8N9B3XRdiyy8E7vOTidzNw8s%2FWtKAIxWbnCzZn4%2FJMBUubLuroBmVIB9UVOMEdD6uzZJXMxBnUatpyAHLu79tlMNqP8TGNMQXXgvSqK5ufzR58ZeivnehV0qE%2FWt1yDEDt%2BfWrmT4mVs6zZWXvqpzmoV3MeygIUCEakh2GAn6rsLT1b2ZsrSkQkrM6F8u7yQFbh%2F0Q7RCSfK2U6tAXQttwc%2FtDK7HYGyvolg%3D%3D;_UP_6D1_64_=069;_UP_A4A_11_=wb9cc1693e1d486b8b2ac58e4839d64e;_UP_D_=mobilectoken=4IUaeDKAfn3pV-MKaWfg_GHG;__pus=76f683009a07bdd1c1a7c04f05838d4bAASVSWCD2jiL0GI43jmIC5x50sk6Tgbh4UtXFf/vqOUyaX8Aory/bsGsGCTl68Lo7sJPpZpBJdif81oItfZizhzH;__kp=d8da7a20-7522-11f0-8e98-7daef001221f;__kps=AATcZArVgS76EPn0FMaV4HEj;__ktd=sii/iz4ePzEaoVirXul7QQ==;__uid=AATcZArVgS76EPn0FMaV4HEj;__puus=b4617d4cdadeb05679ed39be924d99d8AATp/q8/QupT7IiBR1GWqZhx6c42nqKQR/5g49EMGJ5TE742htjk9EkPPibrpvSh8/N8ybuC/Wc4BP1YbLBb5ZFEEOPrmFBTCZtKVEh5HcqkSL6T6LoyeKfvGe32k9HckqoPn9MxbAThhpRlbHg8QgH8OwxLHp0V2cAYcsGY72XN2ZPL7JzZyOAwmSsJwpX9B0+LYNvco+Ixucn5KaaCTKND");
Server.get().start(); Server.get().start();
} }
@ -41,7 +41,7 @@ public class QuarkTest {
@org.junit.Test @org.junit.Test
public void detailContent() throws Exception { public void detailContent() throws Exception {
String content = spider.detailContent(Arrays.asList("https://pan.quark.cn/s/38c5e16d71f7")); String content = spider.detailContent(Arrays.asList("https://pan.quark.cn/s/469c2acf8640"));
System.out.println("detailContent--" + content); System.out.println("detailContent--" + content);
JsonObject map = Json.safeObject(content); JsonObject map = Json.safeObject(content);
Gson gson = new GsonBuilder().setPrettyPrinting().create(); Gson gson = new GsonBuilder().setPrettyPrinting().create();
@ -52,7 +52,7 @@ public class QuarkTest {
@org.junit.Test @org.junit.Test
public void playerContent() throws Exception { public void playerContent() throws Exception {
String content = spider.playerContent("普画","41ea9a50cbdd4e50b019bcd78687ebc1++22fc6fa8350d22e0eaecc49035368e81++38c5e16d71f7++WFcYTmRhjJpKTui56aleYdzBZi9R203GERBVzYNxDxI=",new ArrayList<>()); String content = spider.playerContent("quark原画", "a04522f504a742db8ebaf69e3b7f50d6++375807f3f1068a8fdabac127ec4db89f++469c2acf8640++PVTgPNXNtRFDDkE6SAYX4KPSjk9xl449JkTHl6mtu7k=", new ArrayList<>());
System.out.println("playerContent--" + content); System.out.println("playerContent--" + content);
JsonObject map = Json.safeObject(content); JsonObject map = Json.safeObject(content);
Gson gson = new GsonBuilder().setPrettyPrinting().create(); Gson gson = new GsonBuilder().setPrettyPrinting().create();

View File

@ -0,0 +1,108 @@
import android.app.Application
import com.github.catvod.server.Server
import com.github.catvod.spider.DianYingYunJi
import com.github.catvod.spider.Init
import com.github.catvod.spider.ReBoYingShi
import com.github.catvod.utils.Json
import com.google.gson.GsonBuilder
import org.junit.Assert
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.RuntimeEnvironment
@RunWith(RobolectricTestRunner::class)
class ReBoYSTest {
private var mockContext: Application? = null
private var spider: ReBoYingShi? = null
@Before
@Throws(Exception::class)
fun setUp() {
mockContext = RuntimeEnvironment.application
Init.init(mockContext)
spider = ReBoYingShi()
// spider.init(mockContext, "{\"cookie\":\"b-user-id=89ede34e-0efc-e1dd-c997-f16aaa792d0c; _UP_A4A_11_=wb9661c6dfb642f88f73d8e0c7edd398; b-user-id=89ede34e-0efc-e1dd-c997-f16aaa792d0c; ctoken=wla6p3EUOLyn1FSB8IKp1SEW; grey-id=5583e32b-39df-4bf0-f39f-1adf83f604a2; grey-id.sig=p8ReBIMG2BeZu1sYvsuOAZxYbx-MVrsfKEiCv87MsTM; isQuark=true; isQuark.sig=hUgqObykqFom5Y09bll94T1sS9abT1X-4Df_lzgl8nM; _UP_F7E_8D_=ZkyvVHnrBLp1A1NFJIjWi0PwKLOVbxJPcg0RzQPI6KmBtV6ZMgPh38l93pgubgHDQqhaZ2Sfc0qv%2BRantbfg1mWGAUpRMP4RqXP78Wvu%2FCfvkWWGc5NhCTV71tGOIGgDBR3%2Bu6%2Fjj44KlE5biSNDOWW7Bigcz27lvOTidzNw8s%2FWtKAIxWbnCzZn4%2FJMBUub1SIMcW89g57k4mfPmDlCgpZKzxwl6beSfdtZ4RUWXmZOn5v5NkxVKhU4wR0Pq7NklczEGdRq2nIAcu7v22Uw2o%2FxMY0xBdeC9Korm5%2FNHnxl6K%2Bd6FXSoT9a3XIMQO359auZPiZWzrNlZe%2BqnOahXcx7KAhQIRqSOapSmL4ygJor4r5isJhRuDoXy7vJAVuH%2FRDtEJJ8rZTq0BdC23Bz%2B0MrsdgbK%2BiW; _UP_D_=pc; __wpkreporterwid_=3d3f74a7-99b7-4916-3f78-911fc2eb9d87; tfstk=fIoZNxjnbhKwPOu0TWZ4LsaRqirTcudSSmNbnxD0C5VgClMm8xMyB-GsnSu4tjpOflAOmSD-9PNiGl120XrgkVNb1SrqHbJBN3tSBAEYoQOWVUUg9qZ8n1bGGkD3CqGYINKSBABhjnXgp3_Vywz6gSc0Syj3BWf0mr2DLW24eZfiiovEKWefj1q0swq3E82iNEMinMy7SLrcpA4Fh3z_ZAViCfih3PbtdW5N_DuU77AaTijmYRkL2Wq54ENoy5a7ZXxCbok33XzS7QSZgxD-oyoVsdGotql0p2dVu7umC4nLStbiLmParc4FELHrI-c0u2dPVRrs8zoZWKCnIbNZrlHfUCMUz2z8KyXVSlgSFmUojh58OzeqTzgwaGll4YCYKwctDV5coP2LL79eKHxpNTXHmre1kZU32JPWCR_AkP2LL79eLZQY-WeUNdw1.; __pus=2051c82285199d8be553be41dd5a2100AAQ+mmv35G4FDDZ5x+3Mhe2OMbNgweQ1ODbW8zDt9YuP1LQVqHUuAAz9KWLsPjpNtim0AVGHusN4MCosTmbq/khM; __kp=e6604120-6051-11ef-bfe4-c31b6cdd0766; __kps=AATcZArVgS76EPn0FMaV4HEj; __ktd=sii/iz4ePzEaoVirXul7QQ==; __uid=AATcZArVgS76EPn0FMaV4HEj; __itrace_wid=5829b95d-dac1-48d3-bfd5-f60cd9462786; __puus=7da0b96cb710fa1b376934485f977e05AATp/q8/QupT7IiBR1GWqZhxlIRT677smMvoHlLxQA0Lk6CkP0YJBOTl+p9DZgzlMz6w4hPXPgWsokukk8PW7ZfhFfPmv8tKMgLpCGLW+tk57luhNghmSdTeVPkAF59STtyCPBEtiNzNAd/zZJ6qILJDi5ywEBAAAg+gOyWHoLHNUR+QxeHRuQa8g5WWA95J8jebIlrr8rCvI1vjTbtiYktT\",\"token\":\"26fc6787afff43e78b78992e782502f1\"}");
// spider.init(mockContext, "{\"cookie\":\"ctoken=rldVUeNBAbGyhJdbpC4wEUE-;__pus=75e54cf66f9ea5ed1497838782a90a78AATTBUV9c9w7KXUiHDEl6VdV8Wxki4L9R5kIIjSKQnX1wedJe3s8weva95YKUkRqI1aBY/MA+YBNvaTO0JkXvLp+;__kp=be6b9e10-74f8-11ef-aa08-7d8956cd7603;__kps=AATcZArVgS76EPn0FMaV4HEj;__ktd=sii/iz4ePzEaoVirXul7QQ==;__uid=AATcZArVgS76EPn0FMaV4HEj\"}");
// spider.init(mockContext, "");
/* spider!!.init(
mockContext,
"{\"site\": [\"https://www.wogg.net/\",\"https://wogg.xxooo.cf/\"],\"uccookie\":\"_UP_28A_52_=381;_UP_BT_=html5;_UP_F7E_8D_=ZkyvVHnrBLp1A1NFJIjWi0PwKLOVbxJPcg0RzQPI6Kl8ttcYB1X9Nkx0DnGMyJVLgv0M%2FCztZQaIhZhKaI%2F0Fa%2F5Fqe1t%2BDWF1o9sO71vnupc%2Fvxa%2B78J%2B%2BRZYZzk2EJNXvW0Y4gaAMFHf67r%2BOPjtggEPU7aNnlZbsGKBzPbuW85OJ3M3Dyz9a0oAjFZucLNmfj8kwFS5su6ugGZUgH1RU4wR0Pq7NklczEGdRq2nIAcu7v22Uw2o%2FxMY0xBdeC9Korm5%2FNHnxl6K%2Bd6FXSoT9a3XIMQO359auZPiZWzrNlZe%2BqnOahXcx7KAhQIRqSHYYCfquwtPWx%2FgYBqTnLfzoXy7vJAVuH%2FRDtEJJ8rZTq0BdC23Bz%2B0MrsdgbK%2BiW;_UP_6D1_64_=069;_UP_A4A_11_=wb96b12a16f941809f5af993726ba192;_UP_D_=mobilectoken=oxJV13ITm7aa7_rsplIXC-_v;__pus=cef2cc2dfd5d8af70df36bcedf83995cAAT3ZYhqlLos+yCaVQYYC944c4HEQnHz8uEpdQner0OcqISOpBObxl2kck65MGceRIDBd+MLtDxsNqwXvgDIFpYU;__kp=0d340990-9b39-11ef-ae54-4f733858896a;__kps=AAQXoZxLp9Oe2Ps0d/hNBJl4;__ktd=2gPNadz6Z9c+2+FyQyQZUw==;__uid=AAQXoZxLp9Oe2Ps0d/hNBJl4;UDRIVE_TRANSFER_SESS=UpLXXX2HAXJNW0AHgDcMurpazcqTbU-EQWnKG6RKtkdhdqZgHGTM-BSulf_oo1nmMMjo6hFdByLlm-bEiwjByMbIIEehsxhuuV00b96SSaPExn0wMcQ8SmzJa-YwonEE2MEVWCHcRYuW4Z-ljMOgab7qaGtQUpqjkl-p6OTv23BW-4gM6y7DNKvGeaMv_3NX\"," +
"\"cookie\":\"b-user-id=89ede34e-0efc-e1dd-c997-f16aaa792d0c; _UP_A4A_11_=wb9661c6dfb642f88f73d8e0c7edd398; b-user-id=89ede34e-0efc-e1dd-c997-f16aaa792d0c; ctoken=wla6p3EUOLyn1FSB8IKp1SEW; grey-id=5583e32b-39df-4bf0-f39f-1adf83f604a2; grey-id.sig=p8ReBIMG2BeZu1sYvsuOAZxYbx-MVrsfKEiCv87MsTM; isQuark=true; isQuark.sig=hUgqObykqFom5Y09bll94T1sS9abT1X-4Df_lzgl8nM; _UP_F7E_8D_=ZkyvVHnrBLp1A1NFJIjWi0PwKLOVbxJPcg0RzQPI6KmBtV6ZMgPh38l93pgubgHDQqhaZ2Sfc0qv%2BRantbfg1mWGAUpRMP4RqXP78Wvu%2FCfvkWWGc5NhCTV71tGOIGgDBR3%2Bu6%2Fjj44KlE5biSNDOWW7Bigcz27lvOTidzNw8s%2FWtKAIxWbnCzZn4%2FJMBUub1SIMcW89g57k4mfPmDlCgpZKzxwl6beSfdtZ4RUWXmZOn5v5NkxVKhU4wR0Pq7NklczEGdRq2nIAcu7v22Uw2o%2FxMY0xBdeC9Korm5%2FNHnxl6K%2Bd6FXSoT9a3XIMQO359auZPiZWzrNlZe%2BqnOahXcx7KAhQIRqSOapSmL4ygJor4r5isJhRuDoXy7vJAVuH%2FRDtEJJ8rZTq0BdC23Bz%2B0MrsdgbK%2BiW; _UP_D_=pc; __wpkreporterwid_=3d3f74a7-99b7-4916-3f78-911fc2eb9d87; tfstk=fIoZNxjnbhKwPOu0TWZ4LsaRqirTcudSSmNbnxD0C5VgClMm8xMyB-GsnSu4tjpOflAOmSD-9PNiGl120XrgkVNb1SrqHbJBN3tSBAEYoQOWVUUg9qZ8n1bGGkD3CqGYINKSBABhjnXgp3_Vywz6gSc0Syj3BWf0mr2DLW24eZfiiovEKWefj1q0swq3E82iNEMinMy7SLrcpA4Fh3z_ZAViCfih3PbtdW5N_DuU77AaTijmYRkL2Wq54ENoy5a7ZXxCbok33XzS7QSZgxD-oyoVsdGotql0p2dVu7umC4nLStbiLmParc4FELHrI-c0u2dPVRrs8zoZWKCnIbNZrlHfUCMUz2z8KyXVSlgSFmUojh58OzeqTzgwaGll4YCYKwctDV5coP2LL79eKHxpNTXHmre1kZU32JPWCR_AkP2LL79eLZQY-WeUNdw1.; __pus=2051c82285199d8be553be41dd5a2100AAQ+mmv35G4FDDZ5x+3Mhe2OMbNgweQ1ODbW8zDt9YuP1LQVqHUuAAz9KWLsPjpNtim0AVGHusN4MCosTmbq/khM; __kp=e6604120-6051-11ef-bfe4-c31b6cdd0766; __kps=AATcZArVgS76EPn0FMaV4HEj; __ktd=sii/iz4ePzEaoVirXul7QQ==; __uid=AATcZArVgS76EPn0FMaV4HEj; __itrace_wid=5829b95d-dac1-48d3-bfd5-f60cd9462786; __puus=7da0b96cb710fa1b376934485f977e05AATp/q8/QupT7IiBR1GWqZhxlIRT677smMvoHlLxQA0Lk6CkP0YJBOTl+p9DZgzlMz6w4hPXPgWsokukk8PW7ZfhFfPmv8tKMgLpCGLW+tk57luhNghmSdTeVPkAF59STtyCPBEtiNzNAd/zZJ6qILJDi5ywEBAAAg+gOyWHoLHNUR+QxeHRuQa8g5WWA95J8jebIlrr8rCvI1vjTbtiYktT\",\"token\":\"26fc6787afff43e78b78992e782502f1\"}"
)*/
}
@Test
@Throws(Exception::class)
fun homeContent() {
val content = spider!!.homeContent(true)
val map = Json.safeObject(content)
val gson = GsonBuilder().setPrettyPrinting().create()
println("homeContent--" + gson.toJson(map))
//Assert.assertFalse(map.getAsJsonArray("list").isEmpty());
}
@Test
@Throws(Exception::class)
fun homeVideoContent() {
val content = spider!!.homeVideoContent()
val map = Json.safeObject(content)
val gson = GsonBuilder().setPrettyPrinting().create()
println("homeVideoContent--" + gson.toJson(map))
// Assert.assertFalse(map.getAsJsonArray("list").isEmpty());
}
@Test
@Throws(Exception::class)
fun categoryContent() {
val content = spider!!.categoryContent("https://dyyjpro.com/category/dianying", "2", true, null)
val map = Json.safeObject(content)
val gson = GsonBuilder().setPrettyPrinting().create()
println("categoryContent--" + gson.toJson(map))
Assert.assertFalse(map.getAsJsonArray("list").isEmpty())
}
@Test
@Throws(Exception::class)
fun detailContent() {
val content = spider!!.detailContent(mutableListOf<String?>("/s/家庭煮夫.html"))
println("detailContent--" + content)
val map = Json.safeObject(content)
val gson = GsonBuilder().setPrettyPrinting().create()
println("detailContent--" + gson.toJson(map))
Assert.assertFalse(map.getAsJsonArray("list").isEmpty())
}
@Test
@Throws(Exception::class)
fun playerContent() {
val content = spider!!.playerContent(
"quark4K",
"81c9aa49887d4b07aba861d7dd76d0ac++0ec2d75805f83bd045434f0d22f71489++4be1d75e17aa++wGlrbmw95nBbzO2rbCcEicZ8f4a+z5aKiuyoLQLA5SQ=",
ArrayList<String?>()
)
println("playerContent--" + content)
val map = Json.safeObject(content)
val gson = GsonBuilder().setPrettyPrinting().create()
println("playerContent--" + gson.toJson(map))
Assert.assertFalse(map.getAsJsonPrimitive("url").getAsString().isEmpty())
}
@Test
@Throws(Exception::class)
fun searchContent() {
val content = spider!!.searchContent("屠户之子的科举之路", false)
val map = Json.safeObject(content)
val gson = GsonBuilder().setPrettyPrinting().create()
println("searchContent--" + gson.toJson(map))
Assert.assertFalse(map.getAsJsonArray("list").isEmpty())
}
}

View File

@ -0,0 +1,100 @@
import android.app.Application;
import com.github.catvod.server.Server;
import com.github.catvod.spider.Init;
import com.github.catvod.spider.SeedHub;
import com.github.catvod.spider.Wogg;
import com.github.catvod.utils.Json;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import org.junit.Assert;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.util.ArrayList;
import java.util.Arrays;
@RunWith(RobolectricTestRunner.class)
public class SeedHubTest {
private Application mockContext;
private SeedHub spider;
@org.junit.Before
public void setUp() throws Exception {
mockContext = RuntimeEnvironment.application;
Init.init(mockContext);
spider = new SeedHub();
Server.get().start();
// spider.init(mockContext, "{\"cookie\":\"b-user-id=89ede34e-0efc-e1dd-c997-f16aaa792d0c; _UP_A4A_11_=wb9661c6dfb642f88f73d8e0c7edd398; b-user-id=89ede34e-0efc-e1dd-c997-f16aaa792d0c; ctoken=wla6p3EUOLyn1FSB8IKp1SEW; grey-id=5583e32b-39df-4bf0-f39f-1adf83f604a2; grey-id.sig=p8ReBIMG2BeZu1sYvsuOAZxYbx-MVrsfKEiCv87MsTM; isQuark=true; isQuark.sig=hUgqObykqFom5Y09bll94T1sS9abT1X-4Df_lzgl8nM; _UP_F7E_8D_=ZkyvVHnrBLp1A1NFJIjWi0PwKLOVbxJPcg0RzQPI6KmBtV6ZMgPh38l93pgubgHDQqhaZ2Sfc0qv%2BRantbfg1mWGAUpRMP4RqXP78Wvu%2FCfvkWWGc5NhCTV71tGOIGgDBR3%2Bu6%2Fjj44KlE5biSNDOWW7Bigcz27lvOTidzNw8s%2FWtKAIxWbnCzZn4%2FJMBUub1SIMcW89g57k4mfPmDlCgpZKzxwl6beSfdtZ4RUWXmZOn5v5NkxVKhU4wR0Pq7NklczEGdRq2nIAcu7v22Uw2o%2FxMY0xBdeC9Korm5%2FNHnxl6K%2Bd6FXSoT9a3XIMQO359auZPiZWzrNlZe%2BqnOahXcx7KAhQIRqSOapSmL4ygJor4r5isJhRuDoXy7vJAVuH%2FRDtEJJ8rZTq0BdC23Bz%2B0MrsdgbK%2BiW; _UP_D_=pc; __wpkreporterwid_=3d3f74a7-99b7-4916-3f78-911fc2eb9d87; tfstk=fIoZNxjnbhKwPOu0TWZ4LsaRqirTcudSSmNbnxD0C5VgClMm8xMyB-GsnSu4tjpOflAOmSD-9PNiGl120XrgkVNb1SrqHbJBN3tSBAEYoQOWVUUg9qZ8n1bGGkD3CqGYINKSBABhjnXgp3_Vywz6gSc0Syj3BWf0mr2DLW24eZfiiovEKWefj1q0swq3E82iNEMinMy7SLrcpA4Fh3z_ZAViCfih3PbtdW5N_DuU77AaTijmYRkL2Wq54ENoy5a7ZXxCbok33XzS7QSZgxD-oyoVsdGotql0p2dVu7umC4nLStbiLmParc4FELHrI-c0u2dPVRrs8zoZWKCnIbNZrlHfUCMUz2z8KyXVSlgSFmUojh58OzeqTzgwaGll4YCYKwctDV5coP2LL79eKHxpNTXHmre1kZU32JPWCR_AkP2LL79eLZQY-WeUNdw1.; __pus=2051c82285199d8be553be41dd5a2100AAQ+mmv35G4FDDZ5x+3Mhe2OMbNgweQ1ODbW8zDt9YuP1LQVqHUuAAz9KWLsPjpNtim0AVGHusN4MCosTmbq/khM; __kp=e6604120-6051-11ef-bfe4-c31b6cdd0766; __kps=AATcZArVgS76EPn0FMaV4HEj; __ktd=sii/iz4ePzEaoVirXul7QQ==; __uid=AATcZArVgS76EPn0FMaV4HEj; __itrace_wid=5829b95d-dac1-48d3-bfd5-f60cd9462786; __puus=7da0b96cb710fa1b376934485f977e05AATp/q8/QupT7IiBR1GWqZhxlIRT677smMvoHlLxQA0Lk6CkP0YJBOTl+p9DZgzlMz6w4hPXPgWsokukk8PW7ZfhFfPmv8tKMgLpCGLW+tk57luhNghmSdTeVPkAF59STtyCPBEtiNzNAd/zZJ6qILJDi5ywEBAAAg+gOyWHoLHNUR+QxeHRuQa8g5WWA95J8jebIlrr8rCvI1vjTbtiYktT\",\"token\":\"26fc6787afff43e78b78992e782502f1\"}");
spider.init(mockContext, "{\"site\": [\"https://www.wogg.net/\",\"https://wogg.xxooo.cf/\"],\"uccookie\":\"_UP_28A_52_=381;_UP_BT_=html5;_UP_F7E_8D_=ZkyvVHnrBLp1A1NFJIjWi0PwKLOVbxJPcg0RzQPI6Kl8ttcYB1X9Nkx0DnGMyJVLgv0M%2FCztZQaIhZhKaI%2F0Fa%2F5Fqe1t%2BDWF1o9sO71vnupc%2Fvxa%2B78J%2B%2BRZYZzk2EJNXvW0Y4gaAMFHf67r%2BOPjtggEPU7aNnlZbsGKBzPbuW85OJ3M3Dyz9a0oAjFZucLNmfj8kwFS5su6ugGZUgH1RU4wR0Pq7NklczEGdRq2nIAcu7v22Uw2o%2FxMY0xBdeC9Korm5%2FNHnxl6K%2Bd6FXSoT9a3XIMQO359auZPiZWzrNlZe%2BqnOahXcx7KAhQIRqSHYYCfquwtPWx%2FgYBqTnLfzoXy7vJAVuH%2FRDtEJJ8rZTq0BdC23Bz%2B0MrsdgbK%2BiW;_UP_6D1_64_=069;_UP_A4A_11_=wb96b12a16f941809f5af993726ba192;_UP_D_=mobilectoken=oxJV13ITm7aa7_rsplIXC-_v;__pus=cef2cc2dfd5d8af70df36bcedf83995cAAT3ZYhqlLos+yCaVQYYC944c4HEQnHz8uEpdQner0OcqISOpBObxl2kck65MGceRIDBd+MLtDxsNqwXvgDIFpYU;__kp=0d340990-9b39-11ef-ae54-4f733858896a;__kps=AAQXoZxLp9Oe2Ps0d/hNBJl4;__ktd=2gPNadz6Z9c+2+FyQyQZUw==;__uid=AAQXoZxLp9Oe2Ps0d/hNBJl4;UDRIVE_TRANSFER_SESS=UpLXXX2HAXJNW0AHgDcMurpazcqTbU-EQWnKG6RKtkdhdqZgHGTM-BSulf_oo1nmMMjo6hFdByLlm-bEiwjByMbIIEehsxhuuV00b96SSaPExn0wMcQ8SmzJa-YwonEE2MEVWCHcRYuW4Z-ljMOgab7qaGtQUpqjkl-p6OTv23BW-4gM6y7DNKvGeaMv_3NX\"," +
"\"cookie\":\"b-user-id=89ede34e-0efc-e1dd-c997-f16aaa792d0c; _UP_A4A_11_=wb9661c6dfb642f88f73d8e0c7edd398; b-user-id=89ede34e-0efc-e1dd-c997-f16aaa792d0c; ctoken=wla6p3EUOLyn1FSB8IKp1SEW; grey-id=5583e32b-39df-4bf0-f39f-1adf83f604a2; grey-id.sig=p8ReBIMG2BeZu1sYvsuOAZxYbx-MVrsfKEiCv87MsTM; isQuark=true; isQuark.sig=hUgqObykqFom5Y09bll94T1sS9abT1X-4Df_lzgl8nM; _UP_F7E_8D_=ZkyvVHnrBLp1A1NFJIjWi0PwKLOVbxJPcg0RzQPI6KmBtV6ZMgPh38l93pgubgHDQqhaZ2Sfc0qv%2BRantbfg1mWGAUpRMP4RqXP78Wvu%2FCfvkWWGc5NhCTV71tGOIGgDBR3%2Bu6%2Fjj44KlE5biSNDOWW7Bigcz27lvOTidzNw8s%2FWtKAIxWbnCzZn4%2FJMBUub1SIMcW89g57k4mfPmDlCgpZKzxwl6beSfdtZ4RUWXmZOn5v5NkxVKhU4wR0Pq7NklczEGdRq2nIAcu7v22Uw2o%2FxMY0xBdeC9Korm5%2FNHnxl6K%2Bd6FXSoT9a3XIMQO359auZPiZWzrNlZe%2BqnOahXcx7KAhQIRqSOapSmL4ygJor4r5isJhRuDoXy7vJAVuH%2FRDtEJJ8rZTq0BdC23Bz%2B0MrsdgbK%2BiW; _UP_D_=pc; __wpkreporterwid_=3d3f74a7-99b7-4916-3f78-911fc2eb9d87; tfstk=fIoZNxjnbhKwPOu0TWZ4LsaRqirTcudSSmNbnxD0C5VgClMm8xMyB-GsnSu4tjpOflAOmSD-9PNiGl120XrgkVNb1SrqHbJBN3tSBAEYoQOWVUUg9qZ8n1bGGkD3CqGYINKSBABhjnXgp3_Vywz6gSc0Syj3BWf0mr2DLW24eZfiiovEKWefj1q0swq3E82iNEMinMy7SLrcpA4Fh3z_ZAViCfih3PbtdW5N_DuU77AaTijmYRkL2Wq54ENoy5a7ZXxCbok33XzS7QSZgxD-oyoVsdGotql0p2dVu7umC4nLStbiLmParc4FELHrI-c0u2dPVRrs8zoZWKCnIbNZrlHfUCMUz2z8KyXVSlgSFmUojh58OzeqTzgwaGll4YCYKwctDV5coP2LL79eKHxpNTXHmre1kZU32JPWCR_AkP2LL79eLZQY-WeUNdw1.; __pus=2051c82285199d8be553be41dd5a2100AAQ+mmv35G4FDDZ5x+3Mhe2OMbNgweQ1ODbW8zDt9YuP1LQVqHUuAAz9KWLsPjpNtim0AVGHusN4MCosTmbq/khM; __kp=e6604120-6051-11ef-bfe4-c31b6cdd0766; __kps=AATcZArVgS76EPn0FMaV4HEj; __ktd=sii/iz4ePzEaoVirXul7QQ==; __uid=AATcZArVgS76EPn0FMaV4HEj; __itrace_wid=5829b95d-dac1-48d3-bfd5-f60cd9462786; __puus=7da0b96cb710fa1b376934485f977e05AATp/q8/QupT7IiBR1GWqZhxlIRT677smMvoHlLxQA0Lk6CkP0YJBOTl+p9DZgzlMz6w4hPXPgWsokukk8PW7ZfhFfPmv8tKMgLpCGLW+tk57luhNghmSdTeVPkAF59STtyCPBEtiNzNAd/zZJ6qILJDi5ywEBAAAg+gOyWHoLHNUR+QxeHRuQa8g5WWA95J8jebIlrr8rCvI1vjTbtiYktT\",\"token\":\"26fc6787afff43e78b78992e782502f1\"}");
// spider.init(mockContext, "");
}
@org.junit.Test
public void homeContent() throws Exception {
String content = spider.homeContent(true);
JsonObject map = Json.safeObject(content);
Gson gson = new GsonBuilder().setPrettyPrinting().create();
System.out.println("homeContent--" + gson.toJson(map));
//Assert.assertFalse(map.getAsJsonArray("list").isEmpty());
}
@org.junit.Test
public void homeVideoContent() throws Exception {
String content = spider.homeVideoContent();
JsonObject map = Json.safeObject(content);
Gson gson = new GsonBuilder().setPrettyPrinting().create();
System.out.println("homeVideoContent--" + gson.toJson(map));
// Assert.assertFalse(map.getAsJsonArray("list").isEmpty());
}
@org.junit.Test
public void categoryContent() throws Exception {
String content = spider.categoryContent("2", "2", true, null);
JsonObject map = Json.safeObject(content);
Gson gson = new GsonBuilder().setPrettyPrinting().create();
System.out.println("categoryContent--" + gson.toJson(map));
Assert.assertFalse(map.getAsJsonArray("list").isEmpty());
}
@org.junit.Test
public void detailContent() throws Exception {
String content = spider.detailContent(Arrays.asList("/movies/113317/"));
System.out.println("detailContent--" + content);
JsonObject map = Json.safeObject(content);
Gson gson = new GsonBuilder().setPrettyPrinting().create();
System.out.println("detailContent--" + gson.toJson(map));
Assert.assertFalse(map.getAsJsonArray("list").isEmpty());
}
@org.junit.Test
public void playerContent() throws Exception {
String content = spider.playerContent("uc普画", "52fd84f0bfd44c93a1fca6c76d0b8d16++28d4a0a00ad96562f96506da49b988a0++5ae2d1c474024++yA4L8FmH06i8nfmmY4MwHNSsyhpVzLhzxqDixCp0xm4=", new ArrayList<>());
System.out.println("playerContent--" + content);
JsonObject map = Json.safeObject(content);
Gson gson = new GsonBuilder().setPrettyPrinting().create();
System.out.println("playerContent--" + gson.toJson(map));
Assert.assertFalse(map.getAsJsonPrimitive("url").getAsString().isEmpty());
}
@org.junit.Test
public void searchContent() throws Exception {
String content = spider.searchContent("红海", false);
JsonObject map = Json.safeObject(content);
Gson gson = new GsonBuilder().setPrettyPrinting().create();
System.out.println("searchContent--" + gson.toJson(map));
Assert.assertFalse(map.getAsJsonArray("list").isEmpty());
}
}

View File

@ -1,97 +0,0 @@
import android.app.Application;
import com.github.catvod.spider.DaGongRen;
import com.github.catvod.spider.Init;
import com.github.catvod.spider.Supjav;
import com.github.catvod.utils.Json;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import org.junit.Assert;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.util.ArrayList;
import java.util.Arrays;
@RunWith(RobolectricTestRunner.class)
public class SupjavTest {
// @Mock
private Application mockContext;
private Supjav spider;
@org.junit.Before
public void setUp() throws Exception {
mockContext = RuntimeEnvironment.application;
Init.init(mockContext);
spider = new Supjav();
spider.init(mockContext, "");
}
@org.junit.Test
public void homeContent() throws Exception {
String content = spider.homeContent(true);
JsonObject map = Json.safeObject(content);
Gson gson = new GsonBuilder().setPrettyPrinting().create();
System.out.println("homeContent--" + gson.toJson(map));
//Assert.assertFalse(map.getAsJsonArray("list").isEmpty());
}
@org.junit.Test
public void homeVideoContent() throws Exception {
String content = spider.homeVideoContent();
JsonObject map = Json.safeObject(content);
Gson gson = new GsonBuilder().setPrettyPrinting().create();
System.out.println("homeVideoContent--" + gson.toJson(map));
//Assert.assertFalse(map.getAsJsonArray("list").isEmpty());
}
@org.junit.Test
public void categoryContent() throws Exception {
String content = spider.categoryContent("popular", "2", true, null);
JsonObject map = Json.safeObject(content);
Gson gson = new GsonBuilder().setPrettyPrinting().create();
System.out.println("categoryContent--" + gson.toJson(map));
Assert.assertFalse(map.getAsJsonArray("list").isEmpty());
}
@org.junit.Test
public void detailContent() throws Exception {
String content = spider.detailContent(Arrays.asList("288706.html"));
JsonObject map = Json.safeObject(content);
Gson gson = new GsonBuilder().setPrettyPrinting().create();
System.out.println("detailContent--" + gson.toJson(map));
Assert.assertFalse(map.getAsJsonArray("list").isEmpty());
}
@org.junit.Test
public void playerContent() throws Exception {
String froms = "FST$$$ST$$$VOE";
String urls = "播放$7a39ff703f95e9c01ee6785def2cf624c8636748c26ab4afa219d49d98b7c77ebef268d993e34f9763bc07b2d690fb8d40ec53eb3cf4f17270830acf091765df$$$播放$a8a52ce3124daf8525aeaae8faf1a45e1e74d8b6e1b0db2ee59510d9a238d462be957d62dd34d708391cc6d394bc3bf326eeca43de00d2967a95ac96a12a3b94549b8fd2edab26b7049e1116b32c18c9$$$播放$0a38bff9ae3e860ff7cc5a760f2edd22604306efa019bf6d0adb9874e35d33b8a50955ac4154f2c25c073f23277513045b8c9b26fdfbb54fef4cad7eaf8a6b32"
;
for (int i = 0; i < urls.split("\\$\\$\\$").length; i++) {
String content = spider.playerContent(froms.split("\\$\\$\\$")[i], urls.split("\\$\\$\\$")[i].split("\\$")[1], new ArrayList<>());
JsonObject map = Json.safeObject(content);
Gson gson = new GsonBuilder().setPrettyPrinting().create();
System.out.println("playerContent--" + gson.toJson(map));
// Assert.assertFalse(map.getAsJsonPrimitive("url").getAsString().isEmpty());
}
}
@org.junit.Test
public void searchContent() throws Exception {
String content = spider.searchContent("fc", false);
JsonObject map = Json.safeObject(content);
Gson gson = new GsonBuilder().setPrettyPrinting().create();
System.out.println("searchContent--" + gson.toJson(map));
Assert.assertFalse(map.getAsJsonArray("list").isEmpty());
}
}

View File

@ -1,9 +1,7 @@
import android.app.Application; import android.app.Application;
import com.github.catvod.server.Server; import com.github.catvod.server.Server;
import com.github.catvod.spider.Init; import com.github.catvod.spider.Init;
import com.github.catvod.spider.PanSearch; import com.github.catvod.spider.TgSearchBaidu;
import com.github.catvod.spider.TgSearch;
import com.github.catvod.spider.TianYiSo;
import com.github.catvod.utils.Json; import com.github.catvod.utils.Json;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.GsonBuilder; import com.google.gson.GsonBuilder;
@ -13,6 +11,7 @@ import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner; import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment; import org.robolectric.RuntimeEnvironment;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
@ -20,25 +19,26 @@ public class TgSearchTest {
private Application mockContext; private Application mockContext;
private TgSearch spider; private TgSearchBaidu spider;
@org.junit.Before @org.junit.Before
public void setUp() throws Exception { public void setUp() throws Exception {
mockContext = RuntimeEnvironment.application; mockContext = RuntimeEnvironment.application;
Init.init(mockContext); Init.init(mockContext);
spider = new TgSearch(); spider = new TgSearchBaidu();
Server.get().start(); Server.get().start();
// spider.init(mockContext, "{\"cookie\":\"b-user-id=89ede34e-0efc-e1dd-c997-f16aaa792d0c; _UP_A4A_11_=wb9661c6dfb642f88f73d8e0c7edd398; b-user-id=89ede34e-0efc-e1dd-c997-f16aaa792d0c; ctoken=wla6p3EUOLyn1FSB8IKp1SEW; grey-id=5583e32b-39df-4bf0-f39f-1adf83f604a2; grey-id.sig=p8ReBIMG2BeZu1sYvsuOAZxYbx-MVrsfKEiCv87MsTM; isQuark=true; isQuark.sig=hUgqObykqFom5Y09bll94T1sS9abT1X-4Df_lzgl8nM; _UP_F7E_8D_=ZkyvVHnrBLp1A1NFJIjWi0PwKLOVbxJPcg0RzQPI6KmBtV6ZMgPh38l93pgubgHDQqhaZ2Sfc0qv%2BRantbfg1mWGAUpRMP4RqXP78Wvu%2FCfvkWWGc5NhCTV71tGOIGgDBR3%2Bu6%2Fjj44KlE5biSNDOWW7Bigcz27lvOTidzNw8s%2FWtKAIxWbnCzZn4%2FJMBUub1SIMcW89g57k4mfPmDlCgpZKzxwl6beSfdtZ4RUWXmZOn5v5NkxVKhU4wR0Pq7NklczEGdRq2nIAcu7v22Uw2o%2FxMY0xBdeC9Korm5%2FNHnxl6K%2Bd6FXSoT9a3XIMQO359auZPiZWzrNlZe%2BqnOahXcx7KAhQIRqSOapSmL4ygJor4r5isJhRuDoXy7vJAVuH%2FRDtEJJ8rZTq0BdC23Bz%2B0MrsdgbK%2BiW; _UP_D_=pc; __wpkreporterwid_=3d3f74a7-99b7-4916-3f78-911fc2eb9d87; tfstk=fIoZNxjnbhKwPOu0TWZ4LsaRqirTcudSSmNbnxD0C5VgClMm8xMyB-GsnSu4tjpOflAOmSD-9PNiGl120XrgkVNb1SrqHbJBN3tSBAEYoQOWVUUg9qZ8n1bGGkD3CqGYINKSBABhjnXgp3_Vywz6gSc0Syj3BWf0mr2DLW24eZfiiovEKWefj1q0swq3E82iNEMinMy7SLrcpA4Fh3z_ZAViCfih3PbtdW5N_DuU77AaTijmYRkL2Wq54ENoy5a7ZXxCbok33XzS7QSZgxD-oyoVsdGotql0p2dVu7umC4nLStbiLmParc4FELHrI-c0u2dPVRrs8zoZWKCnIbNZrlHfUCMUz2z8KyXVSlgSFmUojh58OzeqTzgwaGll4YCYKwctDV5coP2LL79eKHxpNTXHmre1kZU32JPWCR_AkP2LL79eLZQY-WeUNdw1.; __pus=2051c82285199d8be553be41dd5a2100AAQ+mmv35G4FDDZ5x+3Mhe2OMbNgweQ1ODbW8zDt9YuP1LQVqHUuAAz9KWLsPjpNtim0AVGHusN4MCosTmbq/khM; __kp=e6604120-6051-11ef-bfe4-c31b6cdd0766; __kps=AATcZArVgS76EPn0FMaV4HEj; __ktd=sii/iz4ePzEaoVirXul7QQ==; __uid=AATcZArVgS76EPn0FMaV4HEj; __itrace_wid=5829b95d-dac1-48d3-bfd5-f60cd9462786; __puus=7da0b96cb710fa1b376934485f977e05AATp/q8/QupT7IiBR1GWqZhxlIRT677smMvoHlLxQA0Lk6CkP0YJBOTl+p9DZgzlMz6w4hPXPgWsokukk8PW7ZfhFfPmv8tKMgLpCGLW+tk57luhNghmSdTeVPkAF59STtyCPBEtiNzNAd/zZJ6qILJDi5ywEBAAAg+gOyWHoLHNUR+QxeHRuQa8g5WWA95J8jebIlrr8rCvI1vjTbtiYktT\",\"token\":\"26fc6787afff43e78b78992e782502f1\"}"); // spider.init(mockContext, "{\"cookie\":\"b-user-id=89ede34e-0efc-e1dd-c997-f16aaa792d0c; _UP_A4A_11_=wb9661c6dfb642f88f73d8e0c7edd398; b-user-id=89ede34e-0efc-e1dd-c997-f16aaa792d0c; ctoken=wla6p3EUOLyn1FSB8IKp1SEW; grey-id=5583e32b-39df-4bf0-f39f-1adf83f604a2; grey-id.sig=p8ReBIMG2BeZu1sYvsuOAZxYbx-MVrsfKEiCv87MsTM; isQuark=true; isQuark.sig=hUgqObykqFom5Y09bll94T1sS9abT1X-4Df_lzgl8nM; _UP_F7E_8D_=ZkyvVHnrBLp1A1NFJIjWi0PwKLOVbxJPcg0RzQPI6KmBtV6ZMgPh38l93pgubgHDQqhaZ2Sfc0qv%2BRantbfg1mWGAUpRMP4RqXP78Wvu%2FCfvkWWGc5NhCTV71tGOIGgDBR3%2Bu6%2Fjj44KlE5biSNDOWW7Bigcz27lvOTidzNw8s%2FWtKAIxWbnCzZn4%2FJMBUub1SIMcW89g57k4mfPmDlCgpZKzxwl6beSfdtZ4RUWXmZOn5v5NkxVKhU4wR0Pq7NklczEGdRq2nIAcu7v22Uw2o%2FxMY0xBdeC9Korm5%2FNHnxl6K%2Bd6FXSoT9a3XIMQO359auZPiZWzrNlZe%2BqnOahXcx7KAhQIRqSOapSmL4ygJor4r5isJhRuDoXy7vJAVuH%2FRDtEJJ8rZTq0BdC23Bz%2B0MrsdgbK%2BiW; _UP_D_=pc; __wpkreporterwid_=3d3f74a7-99b7-4916-3f78-911fc2eb9d87; tfstk=fIoZNxjnbhKwPOu0TWZ4LsaRqirTcudSSmNbnxD0C5VgClMm8xMyB-GsnSu4tjpOflAOmSD-9PNiGl120XrgkVNb1SrqHbJBN3tSBAEYoQOWVUUg9qZ8n1bGGkD3CqGYINKSBABhjnXgp3_Vywz6gSc0Syj3BWf0mr2DLW24eZfiiovEKWefj1q0swq3E82iNEMinMy7SLrcpA4Fh3z_ZAViCfih3PbtdW5N_DuU77AaTijmYRkL2Wq54ENoy5a7ZXxCbok33XzS7QSZgxD-oyoVsdGotql0p2dVu7umC4nLStbiLmParc4FELHrI-c0u2dPVRrs8zoZWKCnIbNZrlHfUCMUz2z8KyXVSlgSFmUojh58OzeqTzgwaGll4YCYKwctDV5coP2LL79eKHxpNTXHmre1kZU32JPWCR_AkP2LL79eLZQY-WeUNdw1.; __pus=2051c82285199d8be553be41dd5a2100AAQ+mmv35G4FDDZ5x+3Mhe2OMbNgweQ1ODbW8zDt9YuP1LQVqHUuAAz9KWLsPjpNtim0AVGHusN4MCosTmbq/khM; __kp=e6604120-6051-11ef-bfe4-c31b6cdd0766; __kps=AATcZArVgS76EPn0FMaV4HEj; __ktd=sii/iz4ePzEaoVirXul7QQ==; __uid=AATcZArVgS76EPn0FMaV4HEj; __itrace_wid=5829b95d-dac1-48d3-bfd5-f60cd9462786; __puus=7da0b96cb710fa1b376934485f977e05AATp/q8/QupT7IiBR1GWqZhxlIRT677smMvoHlLxQA0Lk6CkP0YJBOTl+p9DZgzlMz6w4hPXPgWsokukk8PW7ZfhFfPmv8tKMgLpCGLW+tk57luhNghmSdTeVPkAF59STtyCPBEtiNzNAd/zZJ6qILJDi5ywEBAAAg+gOyWHoLHNUR+QxeHRuQa8g5WWA95J8jebIlrr8rCvI1vjTbtiYktT\",\"token\":\"26fc6787afff43e78b78992e782502f1\"}");
spider.init(mockContext, "{\"tianyicookie\":\"{\\\"open.e.189.cn\\\":[{\\\"name\\\":\\\"SSON\\\",\\\"value\\\":\\\"dc466c8192e3109eaea837c1d136c1fd065253ce1c7d3a66ca1520d7d6d6307b10a1fe65c7becac73b95f24a6e681e654ec4f47c39533ebcc48bb78d6d6e63d1bbf3334e6e97eaa7092d34f87bf1209ee35f344871bc5a329eac34ae948d399d4a6b3b28a929c4f353ade0981657e9e0f09ce27cc1c15d8322c6e45a8ebb21eb431509f1dd7dc3a7856b32b0991d654d5ced73dd20b764ca8737600cbe699c37ccf59b3c610893fc42bdc08b477c5d394e290c14d175d1ca0ee9fa61a1a8dcac7007e9219fd0ae6ccd5dc760524213f85b6b8c6166af01a31336dab797d9118010b81a5a3c26e08e\\\",\\\"expiresAt\\\":253402300799999,\\\"domain\\\":\\\"e.189.cn\\\",\\\"path\\\":\\\"/\\\",\\\"secure\\\":true,\\\"httpOnly\\\":true,\\\"persistent\\\":true,\\\"hostOnly\\\":false},{\\\"name\\\":\\\"GUID\\\",\\\"value\\\":\\\"525d8874e53e46a7ba3ed8907e9fef1f\\\",\\\"expiresAt\\\":1775176321000,\\\"domain\\\":\\\"e.189.cn\\\",\\\"path\\\":\\\"/\\\",\\\"secure\\\":false,\\\"httpOnly\\\":false,\\\"persistent\\\":true,\\\"hostOnly\\\":false},{\\\"name\\\":\\\"pageOp\\\",\\\"value\\\":\\\"336b9ddc820212fa6c9b5a0cfd7bf5b3\\\",\\\"expiresAt\\\":253402300799999,\\\"domain\\\":\\\"e.189.cn\\\",\\\"path\\\":\\\"/\\\",\\\"secure\\\":false,\\\"httpOnly\\\":false,\\\"persistent\\\":false,\\\"hostOnly\\\":false},{\\\"name\\\":\\\"OPENINFO\\\",\\\"value\\\":\\\"33c28688ef52ce9e3a9ef87388047efbde5e3e2e4c7ef6ef267632468c7dfaf294ff59fa59d34801\\\",\\\"expiresAt\\\":253402300799999,\\\"domain\\\":\\\"e.189.cn\\\",\\\"path\\\":\\\"/\\\",\\\"secure\\\":false,\\\"httpOnly\\\":true,\\\"persistent\\\":false,\\\"hostOnly\\\":false},{\\\"name\\\":\\\"GRAYNUMBER\\\",\\\"value\\\":\\\"319DE3F68C8730862F3BEF66F3D635B7\\\",\\\"expiresAt\\\":1775177653000,\\\"domain\\\":\\\"e.189.cn\\\",\\\"path\\\":\\\"/\\\",\\\"secure\\\":false,\\\"httpOnly\\\":false,\\\"persistent\\\":true,\\\"hostOnly\\\":false}],\\\"cloud.189.cn\\\":[{\\\"name\\\":\\\"JSESSIONID\\\",\\\"value\\\":\\\"431787526C43DF21B6373E914FE597EC\\\",\\\"expiresAt\\\":253402300799999,\\\"domain\\\":\\\"cloud.189.cn\\\",\\\"path\\\":\\\"/\\\",\\\"secure\\\":false,\\\"httpOnly\\\":true,\\\"persistent\\\":false,\\\"hostOnly\\\":true},{\\\"name\\\":\\\"COOKIE_LOGIN_USER\\\",\\\"value\\\":\\\"0C7407F59A6E5896EB6B777056E160DB020BAE67B121B5930CCD4777073744055308F7E8CD03F2FC2399E4823F60ECDD74120CEE4C529017\\\",\\\"expiresAt\\\":253402300799999,\\\"domain\\\":\\\"cloud.189.cn\\\",\\\"path\\\":\\\"/\\\",\\\"secure\\\":false,\\\"httpOnly\\\":true,\\\"persistent\\\":false,\\\"hostOnly\\\":false}]}\"}"); /* spider.init(mockContext,"{\n" +
// spider.init(mockContext, ""); "\t\"username\":\"18896781601\" ,\"password\":\"Lushunming@0526\"\n" +
"}"); */
spider.init(mockContext, "{\n" + " \"api_urls\": [\n" + " \"https://psweb.banye.tech:7777/api/search\",\n" + " \"https://so.566987.xyz/api/search\",\n" + " \"http://152.69.222.142:8088/api/search\"\n" + " ],\n" + " \"sources\": [\n" + " \"123盘\"\n" + " ]\n" + " }");
} }
@org.junit.Test @org.junit.Test
public void searchContent() throws Exception { public void searchContent() throws Exception {
String content = spider.searchContent("红海行动", false); String content = spider.searchContent("水饺皇后", false);
JsonObject map = Json.safeObject(content); JsonObject map = Json.safeObject(content);
Gson gson = new GsonBuilder().setPrettyPrinting().create(); Gson gson = new GsonBuilder().setPrettyPrinting().create();
System.out.println("searchContent--" + gson.toJson(map)); System.out.println("searchContent--" + gson.toJson(map));
@ -48,7 +48,18 @@ public class TgSearchTest {
@org.junit.Test @org.junit.Test
public void detailContent() throws Exception { public void detailContent() throws Exception {
String content = spider.detailContent(Arrays.asList("/s/LEvn4lUGB6ufdQ")); String content = spider.detailContent(Arrays.asList("https://123684.com/s/u9izjv-smUWv"));
JsonObject map = Json.safeObject(content);
Gson gson = new GsonBuilder().setPrettyPrinting().create();
System.out.println("detailContent--" + gson.toJson(map));
Assert.assertFalse(map.getAsJsonArray("list").isEmpty());
}
@org.junit.Test
public void playerContent() throws Exception {
String content1 = spider.detailContent(Arrays.asList("https://123684.com/s/u9izjv-smUWv"));
String content = spider.playerContent("pan123原画", "eyJmaWxlbmFtZSI6IlRoZS5EdW1wbGluZy5RdWVlbi4yMDI1LjEwODBwLldFQi1ETC5IMjY0LkFBQy5tcDQiLCJzaGFyZUtleSI6InU5aXpqdi1zbVVXdiIsInNoYXJlUHdkIjoiIiwibmV4dCI6LTEsImZpbGVJZCI6MTg1NjgwODEsIlMzS2V5RmxhZyI6IjE4NDMwNTU4NTItMCIsIlNpemUiOjY0MDQyNTYzMTIsIkV0YWciOiIwYjNjZGIyOTYxZWM2NmQ5MjAyMTViOTRmZGY2MDZjNyJ9", new ArrayList<>());
JsonObject map = Json.safeObject(content); JsonObject map = Json.safeObject(content);
Gson gson = new GsonBuilder().setPrettyPrinting().create(); Gson gson = new GsonBuilder().setPrettyPrinting().create();
System.out.println("detailContent--" + gson.toJson(map)); System.out.println("detailContent--" + gson.toJson(map));

View File

@ -28,8 +28,9 @@ public class TianYiSoTest {
spider = new TianYiSo(); spider = new TianYiSo();
Server.get().start(); Server.get().start();
// spider.init(mockContext, "{\"cookie\":\"b-user-id=89ede34e-0efc-e1dd-c997-f16aaa792d0c; _UP_A4A_11_=wb9661c6dfb642f88f73d8e0c7edd398; b-user-id=89ede34e-0efc-e1dd-c997-f16aaa792d0c; ctoken=wla6p3EUOLyn1FSB8IKp1SEW; grey-id=5583e32b-39df-4bf0-f39f-1adf83f604a2; grey-id.sig=p8ReBIMG2BeZu1sYvsuOAZxYbx-MVrsfKEiCv87MsTM; isQuark=true; isQuark.sig=hUgqObykqFom5Y09bll94T1sS9abT1X-4Df_lzgl8nM; _UP_F7E_8D_=ZkyvVHnrBLp1A1NFJIjWi0PwKLOVbxJPcg0RzQPI6KmBtV6ZMgPh38l93pgubgHDQqhaZ2Sfc0qv%2BRantbfg1mWGAUpRMP4RqXP78Wvu%2FCfvkWWGc5NhCTV71tGOIGgDBR3%2Bu6%2Fjj44KlE5biSNDOWW7Bigcz27lvOTidzNw8s%2FWtKAIxWbnCzZn4%2FJMBUub1SIMcW89g57k4mfPmDlCgpZKzxwl6beSfdtZ4RUWXmZOn5v5NkxVKhU4wR0Pq7NklczEGdRq2nIAcu7v22Uw2o%2FxMY0xBdeC9Korm5%2FNHnxl6K%2Bd6FXSoT9a3XIMQO359auZPiZWzrNlZe%2BqnOahXcx7KAhQIRqSOapSmL4ygJor4r5isJhRuDoXy7vJAVuH%2FRDtEJJ8rZTq0BdC23Bz%2B0MrsdgbK%2BiW; _UP_D_=pc; __wpkreporterwid_=3d3f74a7-99b7-4916-3f78-911fc2eb9d87; tfstk=fIoZNxjnbhKwPOu0TWZ4LsaRqirTcudSSmNbnxD0C5VgClMm8xMyB-GsnSu4tjpOflAOmSD-9PNiGl120XrgkVNb1SrqHbJBN3tSBAEYoQOWVUUg9qZ8n1bGGkD3CqGYINKSBABhjnXgp3_Vywz6gSc0Syj3BWf0mr2DLW24eZfiiovEKWefj1q0swq3E82iNEMinMy7SLrcpA4Fh3z_ZAViCfih3PbtdW5N_DuU77AaTijmYRkL2Wq54ENoy5a7ZXxCbok33XzS7QSZgxD-oyoVsdGotql0p2dVu7umC4nLStbiLmParc4FELHrI-c0u2dPVRrs8zoZWKCnIbNZrlHfUCMUz2z8KyXVSlgSFmUojh58OzeqTzgwaGll4YCYKwctDV5coP2LL79eKHxpNTXHmre1kZU32JPWCR_AkP2LL79eLZQY-WeUNdw1.; __pus=2051c82285199d8be553be41dd5a2100AAQ+mmv35G4FDDZ5x+3Mhe2OMbNgweQ1ODbW8zDt9YuP1LQVqHUuAAz9KWLsPjpNtim0AVGHusN4MCosTmbq/khM; __kp=e6604120-6051-11ef-bfe4-c31b6cdd0766; __kps=AATcZArVgS76EPn0FMaV4HEj; __ktd=sii/iz4ePzEaoVirXul7QQ==; __uid=AATcZArVgS76EPn0FMaV4HEj; __itrace_wid=5829b95d-dac1-48d3-bfd5-f60cd9462786; __puus=7da0b96cb710fa1b376934485f977e05AATp/q8/QupT7IiBR1GWqZhxlIRT677smMvoHlLxQA0Lk6CkP0YJBOTl+p9DZgzlMz6w4hPXPgWsokukk8PW7ZfhFfPmv8tKMgLpCGLW+tk57luhNghmSdTeVPkAF59STtyCPBEtiNzNAd/zZJ6qILJDi5ywEBAAAg+gOyWHoLHNUR+QxeHRuQa8g5WWA95J8jebIlrr8rCvI1vjTbtiYktT\",\"token\":\"26fc6787afff43e78b78992e782502f1\"}"); // spider.init(mockContext, "{\"cookie\":\"b-user-id=89ede34e-0efc-e1dd-c997-f16aaa792d0c; _UP_A4A_11_=wb9661c6dfb642f88f73d8e0c7edd398; b-user-id=89ede34e-0efc-e1dd-c997-f16aaa792d0c; ctoken=wla6p3EUOLyn1FSB8IKp1SEW; grey-id=5583e32b-39df-4bf0-f39f-1adf83f604a2; grey-id.sig=p8ReBIMG2BeZu1sYvsuOAZxYbx-MVrsfKEiCv87MsTM; isQuark=true; isQuark.sig=hUgqObykqFom5Y09bll94T1sS9abT1X-4Df_lzgl8nM; _UP_F7E_8D_=ZkyvVHnrBLp1A1NFJIjWi0PwKLOVbxJPcg0RzQPI6KmBtV6ZMgPh38l93pgubgHDQqhaZ2Sfc0qv%2BRantbfg1mWGAUpRMP4RqXP78Wvu%2FCfvkWWGc5NhCTV71tGOIGgDBR3%2Bu6%2Fjj44KlE5biSNDOWW7Bigcz27lvOTidzNw8s%2FWtKAIxWbnCzZn4%2FJMBUub1SIMcW89g57k4mfPmDlCgpZKzxwl6beSfdtZ4RUWXmZOn5v5NkxVKhU4wR0Pq7NklczEGdRq2nIAcu7v22Uw2o%2FxMY0xBdeC9Korm5%2FNHnxl6K%2Bd6FXSoT9a3XIMQO359auZPiZWzrNlZe%2BqnOahXcx7KAhQIRqSOapSmL4ygJor4r5isJhRuDoXy7vJAVuH%2FRDtEJJ8rZTq0BdC23Bz%2B0MrsdgbK%2BiW; _UP_D_=pc; __wpkreporterwid_=3d3f74a7-99b7-4916-3f78-911fc2eb9d87; tfstk=fIoZNxjnbhKwPOu0TWZ4LsaRqirTcudSSmNbnxD0C5VgClMm8xMyB-GsnSu4tjpOflAOmSD-9PNiGl120XrgkVNb1SrqHbJBN3tSBAEYoQOWVUUg9qZ8n1bGGkD3CqGYINKSBABhjnXgp3_Vywz6gSc0Syj3BWf0mr2DLW24eZfiiovEKWefj1q0swq3E82iNEMinMy7SLrcpA4Fh3z_ZAViCfih3PbtdW5N_DuU77AaTijmYRkL2Wq54ENoy5a7ZXxCbok33XzS7QSZgxD-oyoVsdGotql0p2dVu7umC4nLStbiLmParc4FELHrI-c0u2dPVRrs8zoZWKCnIbNZrlHfUCMUz2z8KyXVSlgSFmUojh58OzeqTzgwaGll4YCYKwctDV5coP2LL79eKHxpNTXHmre1kZU32JPWCR_AkP2LL79eLZQY-WeUNdw1.; __pus=2051c82285199d8be553be41dd5a2100AAQ+mmv35G4FDDZ5x+3Mhe2OMbNgweQ1ODbW8zDt9YuP1LQVqHUuAAz9KWLsPjpNtim0AVGHusN4MCosTmbq/khM; __kp=e6604120-6051-11ef-bfe4-c31b6cdd0766; __kps=AATcZArVgS76EPn0FMaV4HEj; __ktd=sii/iz4ePzEaoVirXul7QQ==; __uid=AATcZArVgS76EPn0FMaV4HEj; __itrace_wid=5829b95d-dac1-48d3-bfd5-f60cd9462786; __puus=7da0b96cb710fa1b376934485f977e05AATp/q8/QupT7IiBR1GWqZhxlIRT677smMvoHlLxQA0Lk6CkP0YJBOTl+p9DZgzlMz6w4hPXPgWsokukk8PW7ZfhFfPmv8tKMgLpCGLW+tk57luhNghmSdTeVPkAF59STtyCPBEtiNzNAd/zZJ6qILJDi5ywEBAAAg+gOyWHoLHNUR+QxeHRuQa8g5WWA95J8jebIlrr8rCvI1vjTbtiYktT\",\"token\":\"26fc6787afff43e78b78992e782502f1\"}");
spider.init(mockContext, "{\"tianyicookie\":\"{\\\"open.e.189.cn\\\":[{\\\"name\\\":\\\"SSON\\\",\\\"value\\\":\\\"dc466c8192e3109eaea837c1d136c1fd065253ce1c7d3a66ca1520d7d6d6307b10a1fe65c7becac73b95f24a6e681e654ec4f47c39533ebcc48bb78d6d6e63d1bbf3334e6e97eaa7092d34f87bf1209ee35f344871bc5a329eac34ae948d399d4a6b3b28a929c4f353ade0981657e9e0f09ce27cc1c15d8322c6e45a8ebb21eb431509f1dd7dc3a7856b32b0991d654d5ced73dd20b764ca8737600cbe699c37ccf59b3c610893fc42bdc08b477c5d394e290c14d175d1ca0ee9fa61a1a8dcac7007e9219fd0ae6ccd5dc760524213f85b6b8c6166af01a31336dab797d9118010b81a5a3c26e08e\\\",\\\"expiresAt\\\":253402300799999,\\\"domain\\\":\\\"e.189.cn\\\",\\\"path\\\":\\\"/\\\",\\\"secure\\\":true,\\\"httpOnly\\\":true,\\\"persistent\\\":true,\\\"hostOnly\\\":false},{\\\"name\\\":\\\"GUID\\\",\\\"value\\\":\\\"525d8874e53e46a7ba3ed8907e9fef1f\\\",\\\"expiresAt\\\":1775176321000,\\\"domain\\\":\\\"e.189.cn\\\",\\\"path\\\":\\\"/\\\",\\\"secure\\\":false,\\\"httpOnly\\\":false,\\\"persistent\\\":true,\\\"hostOnly\\\":false},{\\\"name\\\":\\\"pageOp\\\",\\\"value\\\":\\\"336b9ddc820212fa6c9b5a0cfd7bf5b3\\\",\\\"expiresAt\\\":253402300799999,\\\"domain\\\":\\\"e.189.cn\\\",\\\"path\\\":\\\"/\\\",\\\"secure\\\":false,\\\"httpOnly\\\":false,\\\"persistent\\\":false,\\\"hostOnly\\\":false},{\\\"name\\\":\\\"OPENINFO\\\",\\\"value\\\":\\\"33c28688ef52ce9e3a9ef87388047efbde5e3e2e4c7ef6ef267632468c7dfaf294ff59fa59d34801\\\",\\\"expiresAt\\\":253402300799999,\\\"domain\\\":\\\"e.189.cn\\\",\\\"path\\\":\\\"/\\\",\\\"secure\\\":false,\\\"httpOnly\\\":true,\\\"persistent\\\":false,\\\"hostOnly\\\":false},{\\\"name\\\":\\\"GRAYNUMBER\\\",\\\"value\\\":\\\"319DE3F68C8730862F3BEF66F3D635B7\\\",\\\"expiresAt\\\":1775177653000,\\\"domain\\\":\\\"e.189.cn\\\",\\\"path\\\":\\\"/\\\",\\\"secure\\\":false,\\\"httpOnly\\\":false,\\\"persistent\\\":true,\\\"hostOnly\\\":false}],\\\"cloud.189.cn\\\":[{\\\"name\\\":\\\"JSESSIONID\\\",\\\"value\\\":\\\"431787526C43DF21B6373E914FE597EC\\\",\\\"expiresAt\\\":253402300799999,\\\"domain\\\":\\\"cloud.189.cn\\\",\\\"path\\\":\\\"/\\\",\\\"secure\\\":false,\\\"httpOnly\\\":true,\\\"persistent\\\":false,\\\"hostOnly\\\":true},{\\\"name\\\":\\\"COOKIE_LOGIN_USER\\\",\\\"value\\\":\\\"0C7407F59A6E5896EB6B777056E160DB020BAE67B121B5930CCD4777073744055308F7E8CD03F2FC2399E4823F60ECDD74120CEE4C529017\\\",\\\"expiresAt\\\":253402300799999,\\\"domain\\\":\\\"cloud.189.cn\\\",\\\"path\\\":\\\"/\\\",\\\"secure\\\":false,\\\"httpOnly\\\":true,\\\"persistent\\\":false,\\\"hostOnly\\\":false}]}\"}"); spider.init(mockContext,"{\n" +
// spider.init(mockContext, ""); "\t\"username\":\"18896781601\" ,\"password\":\"Lushunming@0526\"\n" +
"}"); // spider.init(mockContext, "");
} }

View File

@ -41,7 +41,7 @@ public class YiDongYunTest {
@org.junit.Test @org.junit.Test
public void detailContent() throws Exception { public void detailContent() throws Exception {
String content = spider.detailContent(Arrays.asList("https://yun.139.com/shareweb/#/w/i/165CkRwb9G885")); String content = spider.detailContent(Arrays.asList("https://caiyun.139.com/w/i/2nQQVZWCR24yf"));
System.out.println("detailContent--" + content); System.out.println("detailContent--" + content);
JsonObject map = Json.safeObject(content); JsonObject map = Json.safeObject(content);
Gson gson = new GsonBuilder().setPrettyPrinting().create(); Gson gson = new GsonBuilder().setPrettyPrinting().create();
@ -49,10 +49,15 @@ public class YiDongYunTest {
Assert.assertFalse(map.getAsJsonArray("list").isEmpty()); Assert.assertFalse(map.getAsJsonArray("list").isEmpty());
} }
/**
* "vod_play_from": "移动(极速)$$$移动(原画)",
* "vod_play_url": "01.mp4$FtgoS0iBwhPFnG-daRU5HJKQ2yBYj8I3x++2nQQVZWCR24yf#02.mp4$FiPG4ttnS-78swp-1sTFG4prNYnLaUvK_++2nQQVZWCR24yf$$$01.mp4$FtB-r8kHUbJFKbzW_r9tJt6YjcTZCVGWR/FtgoS0iBwhPFnG-daRU5HJKQ2yBYj8I3x++2nQQVZWCR24yf#02.mp4$FtB-r8kHUbJFKbzW_r9tJt6YjcTZCVGWR/FiPG4ttnS-78swp-1sTFG4prNYnLaUvK_++2nQQVZWCR24yf"
* @throws Exception
*/
@org.junit.Test @org.junit.Test
public void playerContent() throws Exception { public void playerContent() throws Exception {
String content = spider.playerContent("普画","41ea9a50cbdd4e50b019bcd78687ebc1++22fc6fa8350d22e0eaecc49035368e81++38c5e16d71f7++WFcYTmRhjJpKTui56aleYdzBZi9R203GERBVzYNxDxI=",new ArrayList<>()); String content = spider.playerContent("移动(原画)","FtB-r8kHUbJFKbzW_r9tJt6YjcTZCVGWR/FiPG4ttnS-78swp-1sTFG4prNYnLaUvK_++2nQQVZWCR24yf",new ArrayList<>());
System.out.println("playerContent--" + content); System.out.println("playerContent--" + content);
JsonObject map = Json.safeObject(content); JsonObject map = Json.safeObject(content);
Gson gson = new GsonBuilder().setPrettyPrinting().create(); Gson gson = new GsonBuilder().setPrettyPrinting().create();

View File

@ -0,0 +1,87 @@
package com.github.catvod.api;
import cn.hutool.core.io.FileUtil;
import com.github.catvod.utils.Json;
import com.google.gson.JsonObject;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import java.net.URLEncoder;
@RunWith(RobolectricTestRunner.class)
public class BaiDuYunHandlerTest {
private BaiDuYunHandler baiDuYunHandler;
@Before
public void setUp() {
baiDuYunHandler = BaiDuYunHandler.get();
}
@Test
public void startScan() throws Exception {
// Mock the OkHttp.get method to return a predefined OkResult
// Execute the method under test
FileUtil.writeBytes(baiDuYunHandler.startScan(), "c://qrcode.png");
while (true) {
}
}
/* @Test
public void refreshCookie() throws Exception {
JsonObject obj = Json.safeObject("{\"open.e.189.cn\":{\"OPENINFO\":\"33c28688ef52ce9e3a9ef87388047efbde5e3e2e4c7ef6ef267632468c7dfaf294ff59fa59d34801\",\"pageOp\":\"f73420158c5c010491f1faa4fc91870e\",\"LT\":\"a8900fc0ecae0c59\",\"GUID\":\"b959026ffdf84080ae8567afd9ea4c32\",\"SSON\":\"dc466c8192e3109eaea837c1d136c1fd065253ce1c7d3a66ca1520d7d6d6307b10a1fe65c7becac73b95f24a6e681e654ec4f47c39533ebcc48bb78d6d6e63d1bbf3334e6e97eaa7092d34f87bf1209e256cd4822db68da051a0aeb532d94408c8e50486347fc713813dafc5776a7cfa665ddf96837151232745aa2957fb441d8a79ca7d86f46452060794e6f4b5873ab99ed476629aed2c7b36a44613c92f925dcfd221fce142cd1ecaab667236df697ece293e3ca24030918e5b357bc193118772278748606ade7262bf25ae7527d3c8a059bd48fc08b53b182e61e543a7e9bd1562b50bf80438\"},\"cloud.189.cn\":{\"JSESSIONID\":\"12088774C4B78E632EB944ECA2E6705F\",\"COOKIE_LOGIN_USER\":\"24DA4CBA27A8388982710C2F3D55EFAA84AEE67E9B3EF1B7AC1C565BEEF24C562052CB9B5EAC85E733C10C2704225133CD625407C352ED5D\"}}");
baiDuYunHandler.setCookie(obj);
baiDuYunHandler.refreshCookie();
while (true) {
}
}
@Test
public void download() throws Exception {
// Mock the OkHttp.get method to return a predefined OkResult
// Execute the method under test
}
@Test
public void testgetUUID() throws Exception {
JsonObject uuid = tianYiHandler.getUUID();
System.out.println(uuid);
}
@Test
public void testdownloadQRCode() throws Exception {
*//*
JsonObject uuidInfo = tianYiHandler.getUUID();
String uuid = uuidInfo.get("uuid").getAsString();
byte[] qrCode = tianYiHandler.downloadQRCode(uuid);
FileUtil.writeBytes(qrCode, "c://qrcode.png");
System.out.println(uuid);*//*
String url = "https://cloud.189.cn/api/portal/callbackUnify.action?browserId=dff95dced0b03d9d972d920f03ddd05e&redirectURL=https%3A%2F%2Fcloud.189.cn%2Fweb%2Fredirect.html";
String encode = "https%3A%2F%2Fcloud.189.cn%2Fapi%2Fportal%2FcallbackUnify.action%3FbrowserId%3Ddff95dced0b03d9d972d920f03ddd05e%26redirectURL%3Dhttps%253A%252F%252Fcloud.189.cn%252Fweb%252Fredirect.html";
assert URLEncoder.encode(url, "UTF-8").equals(encode);
}
@Test
public void loginWithPassword() throws Exception {
tianYiHandler.loginWithPassword("18896781601","Lushunming@0526");
System.out.println("1111");
}
*/
}

View File

@ -0,0 +1,78 @@
package com.github.catvod.api
import com.github.catvod.utils.Json
import kotlinx.coroutines.runBlocking
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
@RunWith(RobolectricTestRunner::class)
class BaiduDriveTest {
@Test
fun getShareList() {
runBlocking {
val reslut =
BaiduDrive.processShareLinks(listOf("https://pan.baidu.com/s/1So5RhSmNts0rWKEzjqinhQ?pwd=9527"))
System.out.println(Json.toJson(reslut))
}
}
@Test
fun getBdUid() {
runBlocking {
val reslut = BaiduDrive.getBdUid()
System.out.println(reslut)
}
}
@Test
fun _getSign() {
val jsonStr =
com.github.catvod.utils.Util.base64Decode("eyJ1ayI6IjExMDI4NjAxODI4OTgiLCJmaWQiOjE1NTY5Mzk0MDE4MjQ2NCwic2hhcmVpZCI6IjUzNDc0MDY5NzI0Iiwic3VybCI6IjFmMHk2MFJrWWNTc3A0RXdXb2xLZ0pnIiwicG5hbWUiOiIwNeWbveivrS00Sy7pq5jnoIHnjocubXA0IiwicXR5cGUiOiJwcmV2aWV3In0=")
val obj = Json.safeObject(jsonStr)
runBlocking {
val reslut = BaiduDrive._getSign(obj)
System.out.println(reslut)
}
}
@Test
fun getVideoUrl() {/* val jsonStr =
com.github.catvod.utils.Util.base64Decode("eyJ1ayI6IjExMDI4NjAxODI4OTgiLCJmaWQiOjE1NTY5Mzk0MDE4MjQ2NCwic2hhcmVpZCI6IjUzNDc0MDY5NzI0Iiwic3VybCI6IjFmMHk2MFJrWWNTc3A0RXdXb2xLZ0pnIiwicG5hbWUiOiIwNeWbveivrS00Sy7pq5jnoIHnjocubXA0IiwicXR5cGUiOiJwcmV2aWV3In0=")
val obj = Json.safeObject(jsonStr)
runBlocking {
val reslut = yunDrive?.getVideoUrl(obj)
System.out.println(reslut)
}*/
val jsonStr =
com.github.catvod.utils.Util.base64Decode("eyJ1ayI6IjI0MDAxMjE2NzIiLCJzaGFyZWlkIjoiMjc2NTk2OTA4MTAiLCJmaWQiOjcxNzUwMDM4OTg1MjYzOSwicmFuZHNrIjoiNEdjMzFTejVsZHNpdHcwRW12ZDNzam9XYWFuVjFEQlFsUHk3VkdESHklMkI0JTNEIiwicG5hbWUiOiJUaGUuUmV0dXJuLm9mLnRoZS5MYW1lLkhlcm8uMjAyNS4yMTYwcC5XRUItREwuSDI2NS5IRFIuNjBmcHMuRERQNS4xLURyZWFtSEQubWt2IiwicXR5cGUiOiJvcmlnaW5hbCJ9")
val obj = Json.safeObject(jsonStr)
runBlocking {
val reslut = BaiduDrive.getVideoUrl(obj, "BD原画1")
System.out.println(reslut)
}
}
@Test
fun createSaveDir() {/* val jsonStr =
com.github.catvod.utils.Util.base64Decode("eyJ1ayI6IjExMDI4NjAxODI4OTgiLCJmaWQiOjE1NTY5Mzk0MDE4MjQ2NCwic2hhcmVpZCI6IjUzNDc0MDY5NzI0Iiwic3VybCI6IjFmMHk2MFJrWWNTc3A0RXdXb2xLZ0pnIiwicG5hbWUiOiIwNeWbveivrS00Sy7pq5jnoIHnjocubXA0IiwicXR5cGUiOiJwcmV2aWV3In0=")
val obj = Json.safeObject(jsonStr)
runBlocking {
val reslut = yunDrive?.getVideoUrl(obj)
System.out.println(reslut)
}*/
runBlocking {
val reslut = BaiduDrive.createSaveDir()
System.out.println(reslut)
}
}
}

View File

@ -0,0 +1,75 @@
package com.github.catvod.api
import com.github.catvod.utils.Util
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
@RunWith(RobolectricTestRunner::class)
class Pan123Test {
var pan123: Pan123Api? = null
@Before
fun setUp() {
pan123 = Pan123Api
// pan123!!.login()
}
@Test
@Throws(Exception::class)
fun login() {
//pan123!!.login()
"https://123684.com/s/u9izjv-smUWv".matches(Pan123Api.regex.toRegex())
"https://www.123865.com/s/u9izjv-6hSWv".matches(Util.patternQuark.toRegex())
}
@Test
@Throws(Exception::class)
fun processShareData() {
val result: Map<String, String> = pan123!!.getShareData("https://www.123865.com/s/u9izjv-6hSWv")
println(result)
val files= pan123!!.getFilesByShareUrl(result["key"]!!, result["sharePwd"]!!);
println(files)
/* if (files != null) {
for (file in files) {
val playUrl = pan123!!.getDownload(result["key"]!!, file.fileId, file.S3KeyFlag, file.Size, file.Etag)
println(playUrl)
}
}*/ if (files != null) {
for (file in files) {
val playUrl = pan123!!.getLiveTranscoding(result["key"]!!, file.fileId, file.S3KeyFlag, file.Size, file.Etag)
println(playUrl)
}
}
/*for (String s : result.keySet()) {
for (Map<String, String> stringStringMap : result.get(s)) {
String playUrl = pan123.fetchPlayUrl(stringStringMap.get("contentId"), "");
System.out.println(stringStringMap.get("name") + ":" + playUrl);
}
}*/
} /* @Test
public void download() throws Exception {
Map<String, List<Map<String, String>>> result = pan123.processShareData("https://caiyun.139.com/w/i/2nQQVZWCR24yf");
System.out.println(result);
for (String s : result.keySet()) {
for (Map<String, String> stringStringMap : result.get(s)) {
String playUrl = pan123.fetchPlayUrl(stringStringMap.get("contentId"), stringStringMap.get("linkID"));
String url2 = pan123.get4kVideoInfo(stringStringMap.get("linkID"), stringStringMap.get("path"));
System.out.println(stringStringMap.get("name") + ":" + playUrl);
System.out.println(stringStringMap.get("url2") + ":" + url2);
}
}
//;
}
*/
}

View File

@ -32,7 +32,7 @@ public class QuarkApiTest {
public void testdownload() throws Exception { public void testdownload() throws Exception {
String url = "https://video-play-p-zb.cdn.yun.cn/P7r95SEr/1997440970/7986fbd7419840ba83d70e7ec36f933867d2fadf/67d2fadf98f5dd83fcd64481858236a79b4c3384?auth_key=1741952223-3304496-16098-d233ccbc65c0321102d36db56f3db9c2&sp=642&token=3-08917a23ee79367eab5e9dcfbd898751-3-2-963-5cbf_3bd039d6d54ec6a8737515d6f20a488c-0-0-0-0-e9fc047ff0aa9b590be130819e1e82f2&ud=9-0-1-2-1-5-8-N-0-4-0-N"; String url = "https://video-play-p-zb.cdn.yun.cn/P7r95SEr/1997440970/7986fbd7419840ba83d70e7ec36f933867d2fadf/67d2fadf98f5dd83fcd64481858236a79b4c3384?auth_key=1741952223-3304496-16098-d233ccbc65c0321102d36db56f3db9c2&sp=642&token=3-08917a23ee79367eab5e9dcfbd898751-3-2-963-5cbf_3bd039d6d54ec6a8737515d6f20a488c-0-0-0-0-e9fc047ff0aa9b590be130819e1e82f2&ud=9-0-1-2-1-5-8-N-0-4-0-N";
OkResult okResult1 = OkHttp.get(url, new HashMap<>(), Map.of("Range", "bytes=0-0")); OkResult okResult1 = OkHttp.get(url, new HashMap<>(), Map.of("Range", "bytes=0-"));
assert okResult1.getCode() == 206; assert okResult1.getCode() == 206;

View File

@ -21,9 +21,9 @@ public class TianyiApiTest {
@Test @Test
public void getShareData() throws Exception { public void getShareData() throws Exception {
com.github.catvod.bean.tianyi.ShareData shareData = TianyiApi.get().getShareData("https://cloud.189.cn/web/share?code=ZvEjUvq6FNr2", ""); // com.github.catvod.bean.tianyi.ShareData shareData = TianyiApi.get().getShareData("https://cloud.189.cn/web/share?code=ZvEjUvq6FNr2", "");
// TianyiApi.get().getVod(shareData); // TianyiApi.get().getVod(shareData);
com.github.catvod.bean.tianyi.ShareData shareData1 = TianyiApi.get().getShareData("https://cloud.189.cn/web/share?code=2eyARfBzURZj访问码kz6y", ""); com.github.catvod.bean.tianyi.ShareData shareData1 = TianyiApi.get().getShareData("http://cloud.189.cn/t/3uIFJrzIFJV3访问码qf4b", "");
// TianyiApi.get().getVod(shareData1); // TianyiApi.get().getVod(shareData1);
ShareData shareData2 = TianyiApi.get().getShareData("https://cloud.189.cn/t/ZvEjUvq6FNr2", ""); ShareData shareData2 = TianyiApi.get().getShareData("https://cloud.189.cn/t/ZvEjUvq6FNr2", "");

View File

@ -1,6 +1,7 @@
package com.github.catvod.api; package com.github.catvod.api;
import com.github.catvod.bean.tianyi.ShareData; import com.github.catvod.bean.tianyi.ShareData;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@ -35,6 +36,24 @@ public class YunDriveTest {
} }
}
@Test
public void download() throws Exception {
Map<String, List<Map<String, String>>> result = yunDrive.processShareData("https://caiyun.139.com/w/i/2nQQVZWCR24yf");
System.out.println(result);
for (String s : result.keySet()) {
for (Map<String, String> stringStringMap : result.get(s)) {
String playUrl = yunDrive.fetchPlayUrl(stringStringMap.get("contentId"), stringStringMap.get("linkID"));
String url2 = yunDrive.get4kVideoInfo(stringStringMap.get("linkID"), stringStringMap.get("path"));
System.out.println(stringStringMap.get("name") + ":" + playUrl);
System.out.println(stringStringMap.get("url2") + ":" + url2);
}
}
//;
} }
@ -48,4 +67,5 @@ public class YunDriveTest {
} }
} }

View File

@ -0,0 +1,29 @@
package com.github.catvod.utils;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import java.util.HashMap;
import java.util.Map;
@RunWith(RobolectricTestRunner.class)
public class ProxyVideoTest {
@Test
public void proxyMultiThread() {
// ProxyVideo.proxyMultiThread()
/* Server.get().start();
String url = ProxyVideo.buildCommonProxyUrl(
// "https://js.shipin520.com/pc/images/new/banner20250225.mp4", new HashMap<>());
"http://172.16.1.217:18089/ng-grid/video.mp4", new HashMap<>());
System.out.println(url);*/
System.out.println(ProxyServer.INSTANCE.buildProxyUrl("http://172.16.1.217:18089/ng-grid/video.mp4", Map.of("header","2","header2","2")));
ProxyServer.INSTANCE.start();
while (true) {
}
}
}

View File

@ -1,5 +1,5 @@
@echo off @echo off
call "%~dp0\gradlew" clean
call "%~dp0\gradlew" assembleRelease --no-daemon call "%~dp0\gradlew" assembleRelease --no-daemon

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -1 +1 @@
4a9814b359e0f39cf4613cd801871c06 5cca7cdb874fb79dee68e08a876fc8ec

View File

@ -1,5 +1,5 @@
{ {
"spider": "https://androidcatvodspider.netlify.app/jar/custom_spider.jar;md5;4a9814b359e0f39cf4613cd801871c06", "spider": "http://gh.halonice.com/https://raw.githubusercontent.com/lushunming/AndroidCatVodSpider/refs/heads/multiThread/jar/custom_spider.jar;md5;5cca7cdb874fb79dee68e08a876fc8ec",
"lives": [ "lives": [
{ {
"name": "电视直播", "name": "电视直播",
@ -66,7 +66,15 @@
"changeable": 1, "changeable": 1,
"ext": "{\"site\": [\"https://www.wogg.net/\",\"https://wogg.xxooo.cf/\"]}" "ext": "{\"site\": [\"https://www.wogg.net/\",\"https://wogg.xxooo.cf/\"]}"
}, },
{
"key": "SeedHub",
"name": "SeedHub",
"type": 3,
"api": "csp_SeedHub",
"searchable": 1,
"changeable": 1,
"ext": {}
},
{ {
"key": "PanTa", "key": "PanTa",
"name": "盘他|139Pan", "name": "盘他|139Pan",
@ -93,15 +101,146 @@
"searchable": 1, "searchable": 1,
"changeable": 1, "changeable": 1,
"ext": {} "ext": {}
}, { },
"key": "TgSearch", {
"name": "TgSearch|Pan", "key": "Qupans",
"name": "Qupans|Pan",
"type": 3, "type": 3,
"api": "csp_TgSearch", "api": "csp_Qupans",
"searchable": 1, "searchable": 1,
"changeable": 1, "changeable": 1,
"ext": {} "ext": {}
}, },
{
"key": "Tg123Search",
"name": "Tg123Search",
"type": 3,
"api": "csp_Tg123Search",
"searchable": 1,
"changeable": 1,
"ext": {}
},
{
"key": "TgbaiduSearch",
"name": "TgbaiduSearch",
"type": 3,
"api": "csp_TgbaiduSearch",
"searchable": 1,
"changeable": 1,
"ext": {}
},
{
"key": "Tg189Search",
"name": "Tg189Search",
"type": 3,
"api": "csp_Tg189Search",
"searchable": 1,
"changeable": 1,
"ext": {}
},
{
"key": "TgQuarkSearch",
"name": "TgQuarkSearch",
"type": 3,
"api": "csp_TgQuarkSearch",
"searchable": 1,
"changeable": 1,
"ext": {}
},{
"key": "TgSearchQuark",
"name": "☁TgSearchQuark┃网盘",
"type": 3,
"api": "csp_TgSearchQuark",
"searchable": 1,
"filterable": 0,
"ext": {
"api_urls": [
"https://api.jkai.de/api/search",
"https://so.252035.xyz/api/search",
"https://psweb.banye.tech:7777/api/search",
"https://so.566987.xyz/api/search",
"http://152.69.222.142:8088/api/search"
],
"sources": [
"夸克"
]
}
},
{
"key": "TgSearchBaidu",
"name": "☁TgSearchBaidu┃网盘",
"type": 3,
"api": "csp_TgSearchBaidu",
"searchable": 1,
"filterable": 0,
"ext": {
"api_urls": [
"https://api.jkai.de/api/search","https://so.252035.xyz/api/search",
"https://psweb.banye.tech:7777/api/search",
"https://so.566987.xyz/api/search",
"http://152.69.222.142:8088/api/search"
],
"sources": [
"百度"
]
}
},
{
"key": "TgSearch123",
"name": "☁TgSearch123┃网盘",
"type": 3,
"api": "csp_TgSearch123",
"searchable": 1,
"filterable": 0,
"ext": {
"api_urls": [
"https://api.jkai.de/api/search","https://so.252035.xyz/api/search",
"https://psweb.banye.tech:7777/api/search",
"https://so.566987.xyz/api/search",
"http://152.69.222.142:8088/api/search"
],
"sources": [
"123盘"
]
}
},
{
"key": "TgSearch189",
"name": "☁TgSearch189┃网盘",
"type": 3,
"api": "csp_TgSearch189",
"searchable": 1,
"filterable": 0,
"ext": {
"api_urls": [
"https://api.jkai.de/api/search","https://so.252035.xyz/api/search",
"https://psweb.banye.tech:7777/api/search",
"https://so.566987.xyz/api/search",
"http://152.69.222.142:8088/api/search"
],
"sources": [
"天翼"
]
}
},{
"key": "TgSearch139",
"name": "☁TgSearch139┃网盘",
"type": 3,
"api": "csp_TgSearch139",
"searchable": 1,
"filterable": 0,
"ext": {
"api_urls": [
"https://api.jkai.de/api/search","https://so.252035.xyz/api/search",
"https://psweb.banye.tech:7777/api/search",
"https://so.566987.xyz/api/search",
"http://152.69.222.142:8088/api/search"
],
"sources": [
"移动"
]
}
},
{ {
"key": "KuaKeBa", "key": "KuaKeBa",
"name": "夸克吧", "name": "夸克吧",
@ -129,7 +268,15 @@
"changeable": 1, "changeable": 1,
"ext": {} "ext": {}
}, },
{
"key": "ReBoYingShi",
"name": "热播影视4k",
"type": 3,
"api": "csp_ReBoYingShi",
"searchable": 1,
"changeable": 1,
"ext": {}
},
{ {
"key": "XuanFeng", "key": "XuanFeng",
"name": "旋风影视", "name": "旋风影视",
@ -312,349 +459,32 @@
"timeout": 30 "timeout": 30
}, },
{ {
"key": "newvision", "key": "local",
"name": "(js)新视觉影院(不稳定)", "name": "Local",
"api": "https://androidcatvodspider.netlify.app/json/js/newvision.js", "type": 3,
"timeout": 30, "api": "csp_Local",
"ext": { "searchable": 0,
"box": "TVBox" "changeable": 0
},
"playerType": 0,
"type": 3
}, },
{ {
"key": "kankan70", "key": "market",
"name": "(js)70看看┃📺", "name": "Market",
"api": "https://androidcatvodspider.netlify.app/json/js/kankan70.js", "type": 3,
"timeout": 30, "api": "csp_Market",
"ext": { "searchable": 0,
"box": "TVBox" "changeable": 0,
}, "ext": "./market.json"
"playerType": 0,
"type": 3
}, },
{ {
"key": "jpyy", "key": "push_agent",
"name": "(js)金牌影院", "name": "Push",
"api": "https://androidcatvodspider.netlify.app/json/js/jpyy.js", "type": 3,
"timeout": 30, "api": "csp_Push",
"ext": { "searchable": 0,
"box": "TVBox" "changeable": 0,
}, "timeout": 60
"playerType": 0,
"type": 3
},
{
"key": "jpyy2",
"name": "(js)金牌影院",
"api": "https://androidcatvodspider.netlify.app/json/js/jpyy2.js",
"timeout": 30,
"ext": {
"box": "TVBox"
},
"playerType": 0,
"type": 3
},
{
"key": "tiantian",
"name": "(js)天天影视┃⛄",
"api": "https://androidcatvodspider.netlify.app/json/js/tiantian.js",
"timeout": 30,
"ext": {
"box": "TVBox"
},
"playerType": 0,
"type": 3
},
{
"key": "xb6v",
"name": "(js)磁力新6V┃🧲",
"api": "https://androidcatvodspider.netlify.app/json/js/xb6v.js",
"timeout": 30,
"ext": {
"box": "TVBox"
},
"playerType": 0,
"type": 3
},
{
"key": "mp4movie",
"name": "(js)Mp4电影┃🍚",
"api": "https://androidcatvodspider.netlify.app/json/js/mp4movie.js",
"timeout": 30,
"ext": {
"box": "TVBox"
},
"playerType": 0,
"type": 3
},
{
"key": "jianpian",
"name": "(js)荐片┃🌼",
"api": "https://androidcatvodspider.netlify.app/json/js/jianpian.js",
"timeout": 30,
"ext": {
"box": "TVBox"
},
"playerType": 0,
"type": 3
},
{
"key": "alipansou",
"name": "(js)阿里猫狸┃😸",
"api": "https://androidcatvodspider.netlify.app/json/js/alipansou.js",
"timeout": 30,
"ext": {
"box": "TVBox",
"aliToken": "26fc6787afff43e78b78992e782502f1",
"quarkCookie": "_UP_A4A_11_=wb965111521e45ffa80410c24a071a54; _UP_D_=pc; tfstk=fXFith4nnRk114LAjWc1TT-OQUXL5hGjqodxDjnVLDoBBchYujR4Drht3GaYxmqYlcPtWc34mknIMcFTB-Y_tuAv6G6_uIcxggIRw_U15jGV2EjCXmnslyoqlSMN9PGjgMEW0dR85uVOAjYmgwcEoqOqgIrqLyoIlq-ZuC738DgqgCJZgH8EuqxZNmAqqSPQTaC3h7bb2rFnSvW87D8jTW0iX0zasIR2zVDi4Poh2svabvzjnSTXixaaFogzbhS-Cry3xVcc9dlz--roR55Jj2wT8znUrEdYrfV3t-kh71znscDo-vYWpf24fSD_IE_78frQF0MNdMg367HmVvxFbyUnbY20XMOqX84UxYFpvQhbA-rqok-G4A9eUc4wG27YtK9jQ2gnVNJioG_mbu_h-wv5CAuIWgQh-K9jQ2gn2wbHFhMZRVIR.; __pus=c81f57897dafcb65d4ecb501bc299199AARcqF72zsatdbsCbiT3qVqsk36caaycoPQW7hz8rbEf+UY7f5aGgH1e90lsONAUwCAW8y27u5A/KXyYqkHCWgjS; __kp=99fa2760-1669-11ef-90cf-8f7a59c3b86e; __kps=AATSt4xuf6r6bqes3LdJvxvy; __ktd=c2e+aLICIvFoeklXXz36VA==; __uid=AATSt4xuf6r6bqes3LdJvxvy; Video-Auth=smob3MOUslklDq2MutANJYZCVo50sLv0GFelx3+cu1nK2fkdL2kvkdpT5yNOhNz0NLTyi5ThWRL47+ztJA4kXQ==; __puus=72f667c533c9a22496f88d2f1bb7ae71AAQ7mrvFw7s9AUPUXvnuGPkcDU3RRTVPdYaYQfsM9Cje2doYXgRZXbImg02EaUaEG+G9ikpo3xubGGdElArOuYvUtJzIXb6yHDnSZbtEUxkwvjfQRNEnDnVwLQ6LL2ORjRaxa9OUfwk/WppWvy6OcDqQtHYkaqB+Poxn5kFs7ZVdAtX7ZQks1czD+g9gAZjsbeBHxHQ1AP5MGc1s3M4RhwZQ"
},
"playerType": 0,
"type": 3
},
{
"key": "huya",
"name": "(js)虎牙直播┃🐯",
"api": "https://androidcatvodspider.netlify.app/json/js/huya.js",
"timeout": 30,
"ext": {
"box": "TVBox"
},
"playerType": 0,
"type": 3
},
{
"key": "star",
"name": "(js)星视界┃墙☄️",
"api": "https://androidcatvodspider.netlify.app/json/js/star.js",
"timeout": 30,
"ext": {
"box": "TVBox"
},
"playerType": 0,
"type": 3
},
{
"key": "nivod",
"name": "(js)泥视频┃墙👑",
"api": "https://androidcatvodspider.netlify.app/json/js/nivod.js",
"timeout": 30,
"ext": {
"box": "TVBox",
"code": 0
},
"playerType": 0,
"type": 3
},
{
"key": "aiyingshi",
"name": "(js)爱影视┃🚀",
"api": "https://androidcatvodspider.netlify.app/json/js/aiyingshi.js",
"timeout": 30,
"ext": {
"box": "TVBox"
},
"playerType": 0,
"type": 3
},
{
"key": "sp360",
"name": "(js)360影视┃🥎",
"api": "https://androidcatvodspider.netlify.app/json/js/sp360.js",
"timeout": 30,
"ext": {
"box": "TVBox"
},
"playerType": 0,
"type": 3
},
{
"key": "mxanime",
"name": "(js)MX动漫┃🍒",
"api": "https://androidcatvodspider.netlify.app/json/js/mxanime.js",
"timeout": 30,
"ext": {
"box": "TVBox"
},
"playerType": 0,
"type": 3
},
{
"key": "cntv",
"name": "(js)中央影视┃🤵‍♂️",
"api": "https://androidcatvodspider.netlify.app/json/js/cntv.js",
"timeout": 30,
"ext": {
"box": "TVBox"
},
"playerType": 0,
"type": 3
},
{
"key": "douban",
"name": "(js)豆瓣┃🍥",
"api": "https://androidcatvodspider.netlify.app/json/js/douban.js",
"timeout": 30,
"ext": {
"box": "TVBox"
},
"playerType": 0,
"type": 3
},
{
"key": "pan_search",
"name": "(js)阿里盘搜┃🗂️",
"api": "https://androidcatvodspider.netlify.app/json/js/pan_search.js",
"timeout": 30,
"ext": {
"box": "TVBox",
"aliToken": "26fc6787afff43e78b78992e782502f1",
"quarkCookie": "_UP_A4A_11_=wb965111521e45ffa80410c24a071a54; _UP_D_=pc; tfstk=fXFith4nnRk114LAjWc1TT-OQUXL5hGjqodxDjnVLDoBBchYujR4Drht3GaYxmqYlcPtWc34mknIMcFTB-Y_tuAv6G6_uIcxggIRw_U15jGV2EjCXmnslyoqlSMN9PGjgMEW0dR85uVOAjYmgwcEoqOqgIrqLyoIlq-ZuC738DgqgCJZgH8EuqxZNmAqqSPQTaC3h7bb2rFnSvW87D8jTW0iX0zasIR2zVDi4Poh2svabvzjnSTXixaaFogzbhS-Cry3xVcc9dlz--roR55Jj2wT8znUrEdYrfV3t-kh71znscDo-vYWpf24fSD_IE_78frQF0MNdMg367HmVvxFbyUnbY20XMOqX84UxYFpvQhbA-rqok-G4A9eUc4wG27YtK9jQ2gnVNJioG_mbu_h-wv5CAuIWgQh-K9jQ2gn2wbHFhMZRVIR.; __pus=c81f57897dafcb65d4ecb501bc299199AARcqF72zsatdbsCbiT3qVqsk36caaycoPQW7hz8rbEf+UY7f5aGgH1e90lsONAUwCAW8y27u5A/KXyYqkHCWgjS; __kp=99fa2760-1669-11ef-90cf-8f7a59c3b86e; __kps=AATSt4xuf6r6bqes3LdJvxvy; __ktd=c2e+aLICIvFoeklXXz36VA==; __uid=AATSt4xuf6r6bqes3LdJvxvy; Video-Auth=smob3MOUslklDq2MutANJYZCVo50sLv0GFelx3+cu1nK2fkdL2kvkdpT5yNOhNz0NLTyi5ThWRL47+ztJA4kXQ==; __puus=72f667c533c9a22496f88d2f1bb7ae71AAQ7mrvFw7s9AUPUXvnuGPkcDU3RRTVPdYaYQfsM9Cje2doYXgRZXbImg02EaUaEG+G9ikpo3xubGGdElArOuYvUtJzIXb6yHDnSZbtEUxkwvjfQRNEnDnVwLQ6LL2ORjRaxa9OUfwk/WppWvy6OcDqQtHYkaqB+Poxn5kFs7ZVdAtX7ZQks1czD+g9gAZjsbeBHxHQ1AP5MGc1s3M4RhwZQ"
},
"playerType": 0,
"type": 3
},
{
"key": "bilibili",
"name": "(js)哔哩哔哩┃🏰",
"api": "https://androidcatvodspider.netlify.app/json/js/bilibili.js",
"timeout": 30,
"ext": {
"box": "TVBox",
"cookie": "buvid3=02675249-8ED3-C418-87F5-59E18316459714816infoc; b_nut=1704421014; _uuid=5D435F74-F574-D9AB-62C1-B9294DE465D913102infoc; buvid_fp=e8c5650c749398e9b5cad3f3ddb5081e; buvid4=007E85D1-52C1-7E6E-07CF-837FFBC9349516677-024010502-J5vTDSZDCw4fNnXRejbSVg%3D%3D; rpdid=|()kYJmulRu0J'u~|RRJl)JR; PVID=1; SESSDATA=3be091d3%2C1720332009%2C699ed%2A11CjAcCdwXG5kY1umhCOpQHOn_WP7L9xFBfWO7KKd4BPweodpR6VyIfeNyPiRmkr5jCqsSVjg0R0dZOVVHRUo3RnhPRTZFc3JPbGdiUjFCdHpiRDhiTkticmdKTjVyS1VhbDdvNjFMSDJlbUJydUlRdjFUNGFBNkJlV2ZTa0N1Q1BEVi1QYTQzTUh3IIEC; bili_jct=b0ee7b5d3f27df893545d811d95506d4; DedeUserID=78014638; DedeUserID__ckMd5=4c8c5d65065e468a; enable_web_push=DISABLE; header_theme_version=CLOSE; home_feed_column=5; CURRENT_BLACKGAP=0; CURRENT_FNVAL=4048; b_lsid=75E916AA_18EA1A8D995; bsource=search_baidu; FEED_LIVE_VERSION=V_HEADER_LIVE_NO_POP; browser_resolution=1507-691; bili_ticket=eyJhbGciOiJIUzI1NiIsImtpZCI6InMwMyIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MTIzNjk5MTMsImlhdCI6MTcxMjExMDY1MywicGx0IjotMX0.8zQW_fNTCSBlK_JkHnzu3gDw62wuTK1qgKcbGec3swM; bili_ticket_expires=171236985"
},
"playerType": 0,
"type": 3
},
{
"key": "changzhang",
"name": "(js)厂长直连┃🏭️",
"api": "https://androidcatvodspider.netlify.app/json/js/changzhang.js",
"timeout": 30,
"ext": {
"box": "TVBox",
"aliToken": "26fc6787afff43e78b78992e782502f1",
"quarkCookie": "_UP_A4A_11_=wb965111521e45ffa80410c24a071a54; _UP_D_=pc; tfstk=fXFith4nnRk114LAjWc1TT-OQUXL5hGjqodxDjnVLDoBBchYujR4Drht3GaYxmqYlcPtWc34mknIMcFTB-Y_tuAv6G6_uIcxggIRw_U15jGV2EjCXmnslyoqlSMN9PGjgMEW0dR85uVOAjYmgwcEoqOqgIrqLyoIlq-ZuC738DgqgCJZgH8EuqxZNmAqqSPQTaC3h7bb2rFnSvW87D8jTW0iX0zasIR2zVDi4Poh2svabvzjnSTXixaaFogzbhS-Cry3xVcc9dlz--roR55Jj2wT8znUrEdYrfV3t-kh71znscDo-vYWpf24fSD_IE_78frQF0MNdMg367HmVvxFbyUnbY20XMOqX84UxYFpvQhbA-rqok-G4A9eUc4wG27YtK9jQ2gnVNJioG_mbu_h-wv5CAuIWgQh-K9jQ2gn2wbHFhMZRVIR.; __pus=c81f57897dafcb65d4ecb501bc299199AARcqF72zsatdbsCbiT3qVqsk36caaycoPQW7hz8rbEf+UY7f5aGgH1e90lsONAUwCAW8y27u5A/KXyYqkHCWgjS; __kp=99fa2760-1669-11ef-90cf-8f7a59c3b86e; __kps=AATSt4xuf6r6bqes3LdJvxvy; __ktd=c2e+aLICIvFoeklXXz36VA==; __uid=AATSt4xuf6r6bqes3LdJvxvy; Video-Auth=smob3MOUslklDq2MutANJYZCVo50sLv0GFelx3+cu1nK2fkdL2kvkdpT5yNOhNz0NLTyi5ThWRL47+ztJA4kXQ==; __puus=72f667c533c9a22496f88d2f1bb7ae71AAQ7mrvFw7s9AUPUXvnuGPkcDU3RRTVPdYaYQfsM9Cje2doYXgRZXbImg02EaUaEG+G9ikpo3xubGGdElArOuYvUtJzIXb6yHDnSZbtEUxkwvjfQRNEnDnVwLQ6LL2ORjRaxa9OUfwk/WppWvy6OcDqQtHYkaqB+Poxn5kFs7ZVdAtX7ZQks1czD+g9gAZjsbeBHxHQ1AP5MGc1s3M4RhwZQ"
},
"playerType": 0,
"type": 3
},
{
"key": "wogg",
"name": "(js)阿里玩偶┃💂",
"api": "https://androidcatvodspider.netlify.app/json/js/wogg.js",
"timeout": 30,
"ext": {
"box": "TVBox",
"aliToken": "26fc6787afff43e78b78992e782502f1",
"quarkCookie": "_UP_A4A_11_=wb965111521e45ffa80410c24a071a54; _UP_D_=pc; tfstk=fXFith4nnRk114LAjWc1TT-OQUXL5hGjqodxDjnVLDoBBchYujR4Drht3GaYxmqYlcPtWc34mknIMcFTB-Y_tuAv6G6_uIcxggIRw_U15jGV2EjCXmnslyoqlSMN9PGjgMEW0dR85uVOAjYmgwcEoqOqgIrqLyoIlq-ZuC738DgqgCJZgH8EuqxZNmAqqSPQTaC3h7bb2rFnSvW87D8jTW0iX0zasIR2zVDi4Poh2svabvzjnSTXixaaFogzbhS-Cry3xVcc9dlz--roR55Jj2wT8znUrEdYrfV3t-kh71znscDo-vYWpf24fSD_IE_78frQF0MNdMg367HmVvxFbyUnbY20XMOqX84UxYFpvQhbA-rqok-G4A9eUc4wG27YtK9jQ2gnVNJioG_mbu_h-wv5CAuIWgQh-K9jQ2gn2wbHFhMZRVIR.; __pus=c81f57897dafcb65d4ecb501bc299199AARcqF72zsatdbsCbiT3qVqsk36caaycoPQW7hz8rbEf+UY7f5aGgH1e90lsONAUwCAW8y27u5A/KXyYqkHCWgjS; __kp=99fa2760-1669-11ef-90cf-8f7a59c3b86e; __kps=AATSt4xuf6r6bqes3LdJvxvy; __ktd=c2e+aLICIvFoeklXXz36VA==; __uid=AATSt4xuf6r6bqes3LdJvxvy; Video-Auth=smob3MOUslklDq2MutANJYZCVo50sLv0GFelx3+cu1nK2fkdL2kvkdpT5yNOhNz0NLTyi5ThWRL47+ztJA4kXQ==; __puus=72f667c533c9a22496f88d2f1bb7ae71AAQ7mrvFw7s9AUPUXvnuGPkcDU3RRTVPdYaYQfsM9Cje2doYXgRZXbImg02EaUaEG+G9ikpo3xubGGdElArOuYvUtJzIXb6yHDnSZbtEUxkwvjfQRNEnDnVwLQ6LL2ORjRaxa9OUfwk/WppWvy6OcDqQtHYkaqB+Poxn5kFs7ZVdAtX7ZQks1czD+g9gAZjsbeBHxHQ1AP5MGc1s3M4RhwZQ"
},
"playerType": 0,
"type": 3
},
{
"key": "nangua",
"name": "(js)南瓜影视┃🎃",
"api": "https://androidcatvodspider.netlify.app/json/js/nangua.js",
"timeout": 30,
"ext": {
"box": "TVBox"
},
"playerType": 0,
"type": 3
},
{
"key": "aliyunpanshare",
"name": "(js)阿里云盘分享┃🥏‍",
"api": "https://androidcatvodspider.netlify.app/json/js/aliyunpanshare.js",
"timeout": 30,
"ext": {
"box": "TVBox",
"aliToken": "26fc6787afff43e78b78992e782502f1",
"quarkCookie": "_UP_A4A_11_=wb965111521e45ffa80410c24a071a54; _UP_D_=pc; tfstk=fXFith4nnRk114LAjWc1TT-OQUXL5hGjqodxDjnVLDoBBchYujR4Drht3GaYxmqYlcPtWc34mknIMcFTB-Y_tuAv6G6_uIcxggIRw_U15jGV2EjCXmnslyoqlSMN9PGjgMEW0dR85uVOAjYmgwcEoqOqgIrqLyoIlq-ZuC738DgqgCJZgH8EuqxZNmAqqSPQTaC3h7bb2rFnSvW87D8jTW0iX0zasIR2zVDi4Poh2svabvzjnSTXixaaFogzbhS-Cry3xVcc9dlz--roR55Jj2wT8znUrEdYrfV3t-kh71znscDo-vYWpf24fSD_IE_78frQF0MNdMg367HmVvxFbyUnbY20XMOqX84UxYFpvQhbA-rqok-G4A9eUc4wG27YtK9jQ2gnVNJioG_mbu_h-wv5CAuIWgQh-K9jQ2gn2wbHFhMZRVIR.; __pus=c81f57897dafcb65d4ecb501bc299199AARcqF72zsatdbsCbiT3qVqsk36caaycoPQW7hz8rbEf+UY7f5aGgH1e90lsONAUwCAW8y27u5A/KXyYqkHCWgjS; __kp=99fa2760-1669-11ef-90cf-8f7a59c3b86e; __kps=AATSt4xuf6r6bqes3LdJvxvy; __ktd=c2e+aLICIvFoeklXXz36VA==; __uid=AATSt4xuf6r6bqes3LdJvxvy; Video-Auth=smob3MOUslklDq2MutANJYZCVo50sLv0GFelx3+cu1nK2fkdL2kvkdpT5yNOhNz0NLTyi5ThWRL47+ztJA4kXQ==; __puus=72f667c533c9a22496f88d2f1bb7ae71AAQ7mrvFw7s9AUPUXvnuGPkcDU3RRTVPdYaYQfsM9Cje2doYXgRZXbImg02EaUaEG+G9ikpo3xubGGdElArOuYvUtJzIXb6yHDnSZbtEUxkwvjfQRNEnDnVwLQ6LL2ORjRaxa9OUfwk/WppWvy6OcDqQtHYkaqB+Poxn5kFs7ZVdAtX7ZQks1czD+g9gAZjsbeBHxHQ1AP5MGc1s3M4RhwZQ"
},
"playerType": 0,
"type": 3
},
{
"key": "dubo",
"name": "(js)独播影视┃🛶",
"api": "https://androidcatvodspider.netlify.app/json/js/dubo.js",
"timeout": 30,
"ext": {
"box": "TVBox"
},
"playerType": 0,
"type": 3
},
{
"key": "haiwaikan",
"name": "(js)海外看┃☕墙",
"api": "https://androidcatvodspider.netlify.app/json/js/haiwaikan.js",
"timeout": 30,
"ext": {
"box": "TVBox"
},
"playerType": 0,
"type": 3
},
{
"key": "dygangs",
"name": "(js)电影港┃🏖️",
"api": "https://androidcatvodspider.netlify.app/json/js/dygangs.js",
"timeout": 30,
"ext": {
"box": "TVBox"
},
"playerType": 0,
"type": 3
},
{
"key": "cilixiong",
"name": "(js)磁力熊┃🐻",
"api": "https://androidcatvodspider.netlify.app/json/js/cilixiong.js",
"timeout": 30,
"ext": {
"box": "TVBox"
},
"playerType": 0,
"type": 3
},
{
"key": "lovemovie",
"name": "(js)爱情电影网┃💕",
"api": "https://androidcatvodspider.netlify.app/json/js/lovemovie.js",
"timeout": 30,
"ext": {
"box": "TVBox"
},
"playerType": 0,
"type": 3
},
{
"key": "gitcafe",
"name": "(js)阿里纸条┃🦊",
"api": "https://androidcatvodspider.netlify.app/json/js/gitcafe.js",
"timeout": 30,
"ext": {
"box": "TVBox",
"aliToken": "26fc6787afff43e78b78992e782502f1",
"quarkCookie": "_UP_A4A_11_=wb965111521e45ffa80410c24a071a54; _UP_D_=pc; tfstk=fXFith4nnRk114LAjWc1TT-OQUXL5hGjqodxDjnVLDoBBchYujR4Drht3GaYxmqYlcPtWc34mknIMcFTB-Y_tuAv6G6_uIcxggIRw_U15jGV2EjCXmnslyoqlSMN9PGjgMEW0dR85uVOAjYmgwcEoqOqgIrqLyoIlq-ZuC738DgqgCJZgH8EuqxZNmAqqSPQTaC3h7bb2rFnSvW87D8jTW0iX0zasIR2zVDi4Poh2svabvzjnSTXixaaFogzbhS-Cry3xVcc9dlz--roR55Jj2wT8znUrEdYrfV3t-kh71znscDo-vYWpf24fSD_IE_78frQF0MNdMg367HmVvxFbyUnbY20XMOqX84UxYFpvQhbA-rqok-G4A9eUc4wG27YtK9jQ2gnVNJioG_mbu_h-wv5CAuIWgQh-K9jQ2gn2wbHFhMZRVIR.; __pus=c81f57897dafcb65d4ecb501bc299199AARcqF72zsatdbsCbiT3qVqsk36caaycoPQW7hz8rbEf+UY7f5aGgH1e90lsONAUwCAW8y27u5A/KXyYqkHCWgjS; __kp=99fa2760-1669-11ef-90cf-8f7a59c3b86e; __kps=AATSt4xuf6r6bqes3LdJvxvy; __ktd=c2e+aLICIvFoeklXXz36VA==; __uid=AATSt4xuf6r6bqes3LdJvxvy; Video-Auth=smob3MOUslklDq2MutANJYZCVo50sLv0GFelx3+cu1nK2fkdL2kvkdpT5yNOhNz0NLTyi5ThWRL47+ztJA4kXQ==; __puus=72f667c533c9a22496f88d2f1bb7ae71AAQ7mrvFw7s9AUPUXvnuGPkcDU3RRTVPdYaYQfsM9Cje2doYXgRZXbImg02EaUaEG+G9ikpo3xubGGdElArOuYvUtJzIXb6yHDnSZbtEUxkwvjfQRNEnDnVwLQ6LL2ORjRaxa9OUfwk/WppWvy6OcDqQtHYkaqB+Poxn5kFs7ZVdAtX7ZQks1czD+g9gAZjsbeBHxHQ1AP5MGc1s3M4RhwZQ"
},
"playerType": 0,
"type": 3
},
{
"key": "kuaikan",
"name": "(js)快看视频┃🛥︎",
"api": "https://androidcatvodspider.netlify.app/json/js/kuaikan.js",
"timeout": 30,
"ext": {
"box": "TVBox"
},
"playerType": 0,
"type": 3
} }
], ],
"parses": [ "parses": [
{ {

View File

@ -1,5 +1,5 @@
{ {
"spider": "https://ghproxy.net/https://raw.githubusercontent.com/lushunming/AndroidCatVodSpider/refs/heads/tianyiPassword/jar/custom_spider.jar;md5;bb54c03fd035f011b948b4ccf1273819", "spider": "https://gh.llkk.cc/https://raw.githubusercontent.com/lushunming/AndroidCatVodSpider/123/jar/custom_spider.jar;md5;77ff0d77d6fccecddd174ee82161f3bd",
"lives": [ "lives": [
{ {
"name": "电视直播", "name": "电视直播",
@ -41,611 +41,26 @@
"type": 3, "type": 3,
"api": "csp_Introduce" "api": "csp_Introduce"
}, },
{
"key": "Douban",
"name": " 豆瓣仅推荐",
"type": 3,
"api": "csp_Douban",
"searchable": 0,
"filterable": 1
},
{
"key": "Jianpian",
"name": "荐片视频",
"type": 3,
"api": "csp_Jianpian",
"searchable": 1,
"filterable": 1
},
{
"key": "玩偶",
"name": "玩偶哥哥",
"type": 3,
"api": "csp_Wogg",
"searchable": 1,
"changeable": 1,
"ext": "{\"site\": [\"https://www.wogg.net/\",\"https://wogg.xxooo.cf/\"]}"
},
{ {
"key": "PanTa", "key": "TgSearchBaidu",
"name": "盘他|139Pan", "name": "☁TgSearchBaidu┃网盘",
"type": 3, "type": 3,
"api": "csp_PanTa", "api": "csp_TgSearchBaidu",
"searchable": 1,
"changeable": 1,
"ext": {}
},
{
"key": "BiXin",
"name": "彼心|139Pan",
"type": 3,
"api": "csp_BiXin",
"searchable": 1,
"changeable": 1,
"ext": {}
}, {
"key": "TgSearch",
"name": "TgSearch|Pan",
"type": 3,
"api": "csp_TgSearch",
"searchable": 1,
"changeable": 1,
"ext": {}
},
{
"key": "KuaKeBa",
"name": "夸克吧",
"type": 3,
"api": "csp_KuaKeBa",
"searchable": 1,
"changeable": 1,
"ext": {}
},
{
"key": "DianYingYunJi",
"name": "电影云集",
"type": 3,
"api": "csp_DianYingYunJi",
"searchable": 1,
"changeable": 1,
"ext": {}
},
{
"key": "KuaKeS",
"name": "夸克网盘社",
"type": 3,
"api": "csp_KuaKeS",
"searchable": 1,
"changeable": 1,
"ext": {}
},
{
"key": "XuanFeng",
"name": "旋风影视",
"type": 3,
"api": "csp_XuanFeng",
"searchable": 1,
"changeable": 1,
"ext": {}
},
{
"key": "NCat",
"name": "网飞猫影视",
"type": 3,
"api": "csp_NCat",
"searchable": 1,
"filterable": 1
},
{
"key": "ChangZhang",
"name": "厂长影视",
"type": 3,
"api": "csp_ChangZhang",
"searchable": "1",
"filterable": "0",
"changeable": 0,
"ext": "https://www.czzy.site/"
},
{
"key": "Zxzj",
"name": "在线之家",
"type": 3,
"api": "csp_Zxzj",
"searchable": "1",
"filterable": "0",
"changeable": 0,
"ext": {}
},
{
"key": "TvDy",
"name": "电影天堂影视",
"type": 3,
"api": "csp_TvDy",
"searchable": 1,
"filterable": 1
},
{
"key": "W55Movie",
"name": "555电影",
"type": 3,
"api": "csp_W55Movie",
"searchable": 0,
"filterable": 1,
"ext": "https://w55xy.com/"
},
{
"key": "DaGongRen",
"name": "打工人电影",
"type": 3,
"api": "csp_DaGongRen",
"searchable": 1,
"filterable": 1
},
{
"key": "HkTv",
"name": "TVB云播影视",
"type": 3,
"api": "csp_HkTv",
"searchable": 0,
"filterable": 1,
"ext": "http://www.hktvyb.vip/"
},
{
"key": "NGkt",
"name": "瓜瓜",
"type": 3,
"api": "csp_NG",
"searchable": 1, "searchable": 1,
"filterable": 0, "filterable": 0,
"ext": {}
},
{
"key": "JustLive",
"name": "JustLive直播",
"type": 3,
"api": "csp_JustLive",
"searchable": 1,
"filterable": 1
},
{
"key": "Xb6v",
"name": "新版6V视频",
"type": 3,
"api": "csp_Xb6v",
"searchable": 1,
"filterable": 1
},
{
"key": "ikanbot",
"name": "爱看机器人",
"type": 3,
"api": "csp_Ikanbot",
"searchable": 1,
"filterable": 0,
"ext": "{\"box\": \"TVBox\", \"danmu\": false}"
},
{
"key": "Libvio",
"name": "立播影视",
"type": 3,
"api": "csp_Libvio",
"searchable": 1,
"filterable": 0,
"ext": "{ \"site\": \"https://www.libvio.app\" }"
},
{
"key": "Ddrk",
"name": "低端影视",
"type": 3,
"api": "csp_Ddrk",
"searchable": 1,
"filterable": 0,
"ext": " {\"site\":\"https://ddys.info/\"}"
},
{
"key": "Ysj",
"name": "异世界动漫(不稳定)",
"type": 3,
"api": "csp_Ysj",
"searchable": 1,
"filterable": 1
},
{
"key": "QxiTv",
"name": "七喜影视",
"type": 3,
"api": "csp_QxiTv",
"searchable": 1,
"changeable": 0,
"ext": {}
},
{
"key": "glod",
"name": "金牌 | 影视",
"type": 3,
"api": "csp_Glod",
"searchable": 1,
"changeable": 0,
"ext": {}
},
{
"key": "YunPanBa",
"name": "云盘吧",
"type": 3,
"api": "csp_YunPanBa",
"searchable": 1,
"timeout": 30
},
{
"key": "QiLeSo",
"name": "奇乐搜┃搜索",
"type": 3,
"api": "csp_QiLeSo",
"searchable": 1,
"timeout": 30
},
{
"key": "PanSearch",
"name": "盘搜┃搜索",
"type": 3,
"api": "csp_PanSearch",
"searchable": 1,
"timeout": 30
},
{
"key": "TianYiSo",
"name": "天翼┃搜索",
"type": 3,
"api": "csp_TianYiSo",
"searchable": 1,
"timeout": 30
},
{
"key": "newvision",
"name": "(js)新视觉影院(不稳定)",
"api": "https://androidcatvodspider.netlify.app/json/js/newvision.js",
"timeout": 30,
"ext": { "ext": {
"box": "TVBox" "api_urls": [
}, "https://psweb.banye.tech:7777/api/search",
"playerType": 0, "https://so.566987.xyz/api/search",
"type": 3 "http://152.69.222.142:8088/api/search"
}, ],
{ "sources": [
"key": "kankan70", "123盘"
"name": "(js)70看看┃📺", ]
"api": "https://androidcatvodspider.netlify.app/json/js/kankan70.js",
"timeout": 30,
"ext": {
"box": "TVBox"
},
"playerType": 0,
"type": 3
},
{
"key": "jpyy",
"name": "(js)金牌影院",
"api": "https://androidcatvodspider.netlify.app/json/js/jpyy.js",
"timeout": 30,
"ext": {
"box": "TVBox"
},
"playerType": 0,
"type": 3
},
{
"key": "jpyy2",
"name": "(js)金牌影院",
"api": "https://androidcatvodspider.netlify.app/json/js/jpyy2.js",
"timeout": 30,
"ext": {
"box": "TVBox"
},
"playerType": 0,
"type": 3
},
{
"key": "tiantian",
"name": "(js)天天影视┃⛄",
"api": "https://androidcatvodspider.netlify.app/json/js/tiantian.js",
"timeout": 30,
"ext": {
"box": "TVBox"
},
"playerType": 0,
"type": 3
},
{
"key": "xb6v",
"name": "(js)磁力新6V┃🧲",
"api": "https://androidcatvodspider.netlify.app/json/js/xb6v.js",
"timeout": 30,
"ext": {
"box": "TVBox"
},
"playerType": 0,
"type": 3
},
{
"key": "mp4movie",
"name": "(js)Mp4电影┃🍚",
"api": "https://androidcatvodspider.netlify.app/json/js/mp4movie.js",
"timeout": 30,
"ext": {
"box": "TVBox"
},
"playerType": 0,
"type": 3
},
{
"key": "jianpian",
"name": "(js)荐片┃🌼",
"api": "https://androidcatvodspider.netlify.app/json/js/jianpian.js",
"timeout": 30,
"ext": {
"box": "TVBox"
},
"playerType": 0,
"type": 3
},
{
"key": "alipansou",
"name": "(js)阿里猫狸┃😸",
"api": "https://androidcatvodspider.netlify.app/json/js/alipansou.js",
"timeout": 30,
"ext": {
"box": "TVBox",
"aliToken": "26fc6787afff43e78b78992e782502f1",
"quarkCookie": "_UP_A4A_11_=wb965111521e45ffa80410c24a071a54; _UP_D_=pc; tfstk=fXFith4nnRk114LAjWc1TT-OQUXL5hGjqodxDjnVLDoBBchYujR4Drht3GaYxmqYlcPtWc34mknIMcFTB-Y_tuAv6G6_uIcxggIRw_U15jGV2EjCXmnslyoqlSMN9PGjgMEW0dR85uVOAjYmgwcEoqOqgIrqLyoIlq-ZuC738DgqgCJZgH8EuqxZNmAqqSPQTaC3h7bb2rFnSvW87D8jTW0iX0zasIR2zVDi4Poh2svabvzjnSTXixaaFogzbhS-Cry3xVcc9dlz--roR55Jj2wT8znUrEdYrfV3t-kh71znscDo-vYWpf24fSD_IE_78frQF0MNdMg367HmVvxFbyUnbY20XMOqX84UxYFpvQhbA-rqok-G4A9eUc4wG27YtK9jQ2gnVNJioG_mbu_h-wv5CAuIWgQh-K9jQ2gn2wbHFhMZRVIR.; __pus=c81f57897dafcb65d4ecb501bc299199AARcqF72zsatdbsCbiT3qVqsk36caaycoPQW7hz8rbEf+UY7f5aGgH1e90lsONAUwCAW8y27u5A/KXyYqkHCWgjS; __kp=99fa2760-1669-11ef-90cf-8f7a59c3b86e; __kps=AATSt4xuf6r6bqes3LdJvxvy; __ktd=c2e+aLICIvFoeklXXz36VA==; __uid=AATSt4xuf6r6bqes3LdJvxvy; Video-Auth=smob3MOUslklDq2MutANJYZCVo50sLv0GFelx3+cu1nK2fkdL2kvkdpT5yNOhNz0NLTyi5ThWRL47+ztJA4kXQ==; __puus=72f667c533c9a22496f88d2f1bb7ae71AAQ7mrvFw7s9AUPUXvnuGPkcDU3RRTVPdYaYQfsM9Cje2doYXgRZXbImg02EaUaEG+G9ikpo3xubGGdElArOuYvUtJzIXb6yHDnSZbtEUxkwvjfQRNEnDnVwLQ6LL2ORjRaxa9OUfwk/WppWvy6OcDqQtHYkaqB+Poxn5kFs7ZVdAtX7ZQks1czD+g9gAZjsbeBHxHQ1AP5MGc1s3M4RhwZQ"
},
"playerType": 0,
"type": 3
},
{
"key": "huya",
"name": "(js)虎牙直播┃🐯",
"api": "https://androidcatvodspider.netlify.app/json/js/huya.js",
"timeout": 30,
"ext": {
"box": "TVBox"
},
"playerType": 0,
"type": 3
},
{
"key": "star",
"name": "(js)星视界┃墙☄️",
"api": "https://androidcatvodspider.netlify.app/json/js/star.js",
"timeout": 30,
"ext": {
"box": "TVBox"
},
"playerType": 0,
"type": 3
},
{
"key": "nivod",
"name": "(js)泥视频┃墙👑",
"api": "https://androidcatvodspider.netlify.app/json/js/nivod.js",
"timeout": 30,
"ext": {
"box": "TVBox",
"code": 0
},
"playerType": 0,
"type": 3
},
{
"key": "aiyingshi",
"name": "(js)爱影视┃🚀",
"api": "https://androidcatvodspider.netlify.app/json/js/aiyingshi.js",
"timeout": 30,
"ext": {
"box": "TVBox"
},
"playerType": 0,
"type": 3
},
{
"key": "sp360",
"name": "(js)360影视┃🥎",
"api": "https://androidcatvodspider.netlify.app/json/js/sp360.js",
"timeout": 30,
"ext": {
"box": "TVBox"
},
"playerType": 0,
"type": 3
},
{
"key": "mxanime",
"name": "(js)MX动漫┃🍒",
"api": "https://androidcatvodspider.netlify.app/json/js/mxanime.js",
"timeout": 30,
"ext": {
"box": "TVBox"
},
"playerType": 0,
"type": 3
},
{
"key": "cntv",
"name": "(js)中央影视┃🤵‍♂️",
"api": "https://androidcatvodspider.netlify.app/json/js/cntv.js",
"timeout": 30,
"ext": {
"box": "TVBox"
},
"playerType": 0,
"type": 3
},
{
"key": "douban",
"name": "(js)豆瓣┃🍥",
"api": "https://androidcatvodspider.netlify.app/json/js/douban.js",
"timeout": 30,
"ext": {
"box": "TVBox"
},
"playerType": 0,
"type": 3
},
{
"key": "pan_search",
"name": "(js)阿里盘搜┃🗂️",
"api": "https://androidcatvodspider.netlify.app/json/js/pan_search.js",
"timeout": 30,
"ext": {
"box": "TVBox",
"aliToken": "26fc6787afff43e78b78992e782502f1",
"quarkCookie": "_UP_A4A_11_=wb965111521e45ffa80410c24a071a54; _UP_D_=pc; tfstk=fXFith4nnRk114LAjWc1TT-OQUXL5hGjqodxDjnVLDoBBchYujR4Drht3GaYxmqYlcPtWc34mknIMcFTB-Y_tuAv6G6_uIcxggIRw_U15jGV2EjCXmnslyoqlSMN9PGjgMEW0dR85uVOAjYmgwcEoqOqgIrqLyoIlq-ZuC738DgqgCJZgH8EuqxZNmAqqSPQTaC3h7bb2rFnSvW87D8jTW0iX0zasIR2zVDi4Poh2svabvzjnSTXixaaFogzbhS-Cry3xVcc9dlz--roR55Jj2wT8znUrEdYrfV3t-kh71znscDo-vYWpf24fSD_IE_78frQF0MNdMg367HmVvxFbyUnbY20XMOqX84UxYFpvQhbA-rqok-G4A9eUc4wG27YtK9jQ2gnVNJioG_mbu_h-wv5CAuIWgQh-K9jQ2gn2wbHFhMZRVIR.; __pus=c81f57897dafcb65d4ecb501bc299199AARcqF72zsatdbsCbiT3qVqsk36caaycoPQW7hz8rbEf+UY7f5aGgH1e90lsONAUwCAW8y27u5A/KXyYqkHCWgjS; __kp=99fa2760-1669-11ef-90cf-8f7a59c3b86e; __kps=AATSt4xuf6r6bqes3LdJvxvy; __ktd=c2e+aLICIvFoeklXXz36VA==; __uid=AATSt4xuf6r6bqes3LdJvxvy; Video-Auth=smob3MOUslklDq2MutANJYZCVo50sLv0GFelx3+cu1nK2fkdL2kvkdpT5yNOhNz0NLTyi5ThWRL47+ztJA4kXQ==; __puus=72f667c533c9a22496f88d2f1bb7ae71AAQ7mrvFw7s9AUPUXvnuGPkcDU3RRTVPdYaYQfsM9Cje2doYXgRZXbImg02EaUaEG+G9ikpo3xubGGdElArOuYvUtJzIXb6yHDnSZbtEUxkwvjfQRNEnDnVwLQ6LL2ORjRaxa9OUfwk/WppWvy6OcDqQtHYkaqB+Poxn5kFs7ZVdAtX7ZQks1czD+g9gAZjsbeBHxHQ1AP5MGc1s3M4RhwZQ"
},
"playerType": 0,
"type": 3
},
{
"key": "bilibili",
"name": "(js)哔哩哔哩┃🏰",
"api": "https://androidcatvodspider.netlify.app/json/js/bilibili.js",
"timeout": 30,
"ext": {
"box": "TVBox",
"cookie": "buvid3=02675249-8ED3-C418-87F5-59E18316459714816infoc; b_nut=1704421014; _uuid=5D435F74-F574-D9AB-62C1-B9294DE465D913102infoc; buvid_fp=e8c5650c749398e9b5cad3f3ddb5081e; buvid4=007E85D1-52C1-7E6E-07CF-837FFBC9349516677-024010502-J5vTDSZDCw4fNnXRejbSVg%3D%3D; rpdid=|()kYJmulRu0J'u~|RRJl)JR; PVID=1; SESSDATA=3be091d3%2C1720332009%2C699ed%2A11CjAcCdwXG5kY1umhCOpQHOn_WP7L9xFBfWO7KKd4BPweodpR6VyIfeNyPiRmkr5jCqsSVjg0R0dZOVVHRUo3RnhPRTZFc3JPbGdiUjFCdHpiRDhiTkticmdKTjVyS1VhbDdvNjFMSDJlbUJydUlRdjFUNGFBNkJlV2ZTa0N1Q1BEVi1QYTQzTUh3IIEC; bili_jct=b0ee7b5d3f27df893545d811d95506d4; DedeUserID=78014638; DedeUserID__ckMd5=4c8c5d65065e468a; enable_web_push=DISABLE; header_theme_version=CLOSE; home_feed_column=5; CURRENT_BLACKGAP=0; CURRENT_FNVAL=4048; b_lsid=75E916AA_18EA1A8D995; bsource=search_baidu; FEED_LIVE_VERSION=V_HEADER_LIVE_NO_POP; browser_resolution=1507-691; bili_ticket=eyJhbGciOiJIUzI1NiIsImtpZCI6InMwMyIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MTIzNjk5MTMsImlhdCI6MTcxMjExMDY1MywicGx0IjotMX0.8zQW_fNTCSBlK_JkHnzu3gDw62wuTK1qgKcbGec3swM; bili_ticket_expires=171236985"
},
"playerType": 0,
"type": 3
},
{
"key": "changzhang",
"name": "(js)厂长直连┃🏭️",
"api": "https://androidcatvodspider.netlify.app/json/js/changzhang.js",
"timeout": 30,
"ext": {
"box": "TVBox",
"aliToken": "26fc6787afff43e78b78992e782502f1",
"quarkCookie": "_UP_A4A_11_=wb965111521e45ffa80410c24a071a54; _UP_D_=pc; tfstk=fXFith4nnRk114LAjWc1TT-OQUXL5hGjqodxDjnVLDoBBchYujR4Drht3GaYxmqYlcPtWc34mknIMcFTB-Y_tuAv6G6_uIcxggIRw_U15jGV2EjCXmnslyoqlSMN9PGjgMEW0dR85uVOAjYmgwcEoqOqgIrqLyoIlq-ZuC738DgqgCJZgH8EuqxZNmAqqSPQTaC3h7bb2rFnSvW87D8jTW0iX0zasIR2zVDi4Poh2svabvzjnSTXixaaFogzbhS-Cry3xVcc9dlz--roR55Jj2wT8znUrEdYrfV3t-kh71znscDo-vYWpf24fSD_IE_78frQF0MNdMg367HmVvxFbyUnbY20XMOqX84UxYFpvQhbA-rqok-G4A9eUc4wG27YtK9jQ2gnVNJioG_mbu_h-wv5CAuIWgQh-K9jQ2gn2wbHFhMZRVIR.; __pus=c81f57897dafcb65d4ecb501bc299199AARcqF72zsatdbsCbiT3qVqsk36caaycoPQW7hz8rbEf+UY7f5aGgH1e90lsONAUwCAW8y27u5A/KXyYqkHCWgjS; __kp=99fa2760-1669-11ef-90cf-8f7a59c3b86e; __kps=AATSt4xuf6r6bqes3LdJvxvy; __ktd=c2e+aLICIvFoeklXXz36VA==; __uid=AATSt4xuf6r6bqes3LdJvxvy; Video-Auth=smob3MOUslklDq2MutANJYZCVo50sLv0GFelx3+cu1nK2fkdL2kvkdpT5yNOhNz0NLTyi5ThWRL47+ztJA4kXQ==; __puus=72f667c533c9a22496f88d2f1bb7ae71AAQ7mrvFw7s9AUPUXvnuGPkcDU3RRTVPdYaYQfsM9Cje2doYXgRZXbImg02EaUaEG+G9ikpo3xubGGdElArOuYvUtJzIXb6yHDnSZbtEUxkwvjfQRNEnDnVwLQ6LL2ORjRaxa9OUfwk/WppWvy6OcDqQtHYkaqB+Poxn5kFs7ZVdAtX7ZQks1czD+g9gAZjsbeBHxHQ1AP5MGc1s3M4RhwZQ"
},
"playerType": 0,
"type": 3
},
{
"key": "wogg",
"name": "(js)阿里玩偶┃💂",
"api": "https://androidcatvodspider.netlify.app/json/js/wogg.js",
"timeout": 30,
"ext": {
"box": "TVBox",
"aliToken": "26fc6787afff43e78b78992e782502f1",
"quarkCookie": "_UP_A4A_11_=wb965111521e45ffa80410c24a071a54; _UP_D_=pc; tfstk=fXFith4nnRk114LAjWc1TT-OQUXL5hGjqodxDjnVLDoBBchYujR4Drht3GaYxmqYlcPtWc34mknIMcFTB-Y_tuAv6G6_uIcxggIRw_U15jGV2EjCXmnslyoqlSMN9PGjgMEW0dR85uVOAjYmgwcEoqOqgIrqLyoIlq-ZuC738DgqgCJZgH8EuqxZNmAqqSPQTaC3h7bb2rFnSvW87D8jTW0iX0zasIR2zVDi4Poh2svabvzjnSTXixaaFogzbhS-Cry3xVcc9dlz--roR55Jj2wT8znUrEdYrfV3t-kh71znscDo-vYWpf24fSD_IE_78frQF0MNdMg367HmVvxFbyUnbY20XMOqX84UxYFpvQhbA-rqok-G4A9eUc4wG27YtK9jQ2gnVNJioG_mbu_h-wv5CAuIWgQh-K9jQ2gn2wbHFhMZRVIR.; __pus=c81f57897dafcb65d4ecb501bc299199AARcqF72zsatdbsCbiT3qVqsk36caaycoPQW7hz8rbEf+UY7f5aGgH1e90lsONAUwCAW8y27u5A/KXyYqkHCWgjS; __kp=99fa2760-1669-11ef-90cf-8f7a59c3b86e; __kps=AATSt4xuf6r6bqes3LdJvxvy; __ktd=c2e+aLICIvFoeklXXz36VA==; __uid=AATSt4xuf6r6bqes3LdJvxvy; Video-Auth=smob3MOUslklDq2MutANJYZCVo50sLv0GFelx3+cu1nK2fkdL2kvkdpT5yNOhNz0NLTyi5ThWRL47+ztJA4kXQ==; __puus=72f667c533c9a22496f88d2f1bb7ae71AAQ7mrvFw7s9AUPUXvnuGPkcDU3RRTVPdYaYQfsM9Cje2doYXgRZXbImg02EaUaEG+G9ikpo3xubGGdElArOuYvUtJzIXb6yHDnSZbtEUxkwvjfQRNEnDnVwLQ6LL2ORjRaxa9OUfwk/WppWvy6OcDqQtHYkaqB+Poxn5kFs7ZVdAtX7ZQks1czD+g9gAZjsbeBHxHQ1AP5MGc1s3M4RhwZQ"
},
"playerType": 0,
"type": 3
},
{
"key": "nangua",
"name": "(js)南瓜影视┃🎃",
"api": "https://androidcatvodspider.netlify.app/json/js/nangua.js",
"timeout": 30,
"ext": {
"box": "TVBox"
},
"playerType": 0,
"type": 3
},
{
"key": "aliyunpanshare",
"name": "(js)阿里云盘分享┃🥏‍",
"api": "https://androidcatvodspider.netlify.app/json/js/aliyunpanshare.js",
"timeout": 30,
"ext": {
"box": "TVBox",
"aliToken": "26fc6787afff43e78b78992e782502f1",
"quarkCookie": "_UP_A4A_11_=wb965111521e45ffa80410c24a071a54; _UP_D_=pc; tfstk=fXFith4nnRk114LAjWc1TT-OQUXL5hGjqodxDjnVLDoBBchYujR4Drht3GaYxmqYlcPtWc34mknIMcFTB-Y_tuAv6G6_uIcxggIRw_U15jGV2EjCXmnslyoqlSMN9PGjgMEW0dR85uVOAjYmgwcEoqOqgIrqLyoIlq-ZuC738DgqgCJZgH8EuqxZNmAqqSPQTaC3h7bb2rFnSvW87D8jTW0iX0zasIR2zVDi4Poh2svabvzjnSTXixaaFogzbhS-Cry3xVcc9dlz--roR55Jj2wT8znUrEdYrfV3t-kh71znscDo-vYWpf24fSD_IE_78frQF0MNdMg367HmVvxFbyUnbY20XMOqX84UxYFpvQhbA-rqok-G4A9eUc4wG27YtK9jQ2gnVNJioG_mbu_h-wv5CAuIWgQh-K9jQ2gn2wbHFhMZRVIR.; __pus=c81f57897dafcb65d4ecb501bc299199AARcqF72zsatdbsCbiT3qVqsk36caaycoPQW7hz8rbEf+UY7f5aGgH1e90lsONAUwCAW8y27u5A/KXyYqkHCWgjS; __kp=99fa2760-1669-11ef-90cf-8f7a59c3b86e; __kps=AATSt4xuf6r6bqes3LdJvxvy; __ktd=c2e+aLICIvFoeklXXz36VA==; __uid=AATSt4xuf6r6bqes3LdJvxvy; Video-Auth=smob3MOUslklDq2MutANJYZCVo50sLv0GFelx3+cu1nK2fkdL2kvkdpT5yNOhNz0NLTyi5ThWRL47+ztJA4kXQ==; __puus=72f667c533c9a22496f88d2f1bb7ae71AAQ7mrvFw7s9AUPUXvnuGPkcDU3RRTVPdYaYQfsM9Cje2doYXgRZXbImg02EaUaEG+G9ikpo3xubGGdElArOuYvUtJzIXb6yHDnSZbtEUxkwvjfQRNEnDnVwLQ6LL2ORjRaxa9OUfwk/WppWvy6OcDqQtHYkaqB+Poxn5kFs7ZVdAtX7ZQks1czD+g9gAZjsbeBHxHQ1AP5MGc1s3M4RhwZQ"
},
"playerType": 0,
"type": 3
},
{
"key": "dubo",
"name": "(js)独播影视┃🛶",
"api": "https://androidcatvodspider.netlify.app/json/js/dubo.js",
"timeout": 30,
"ext": {
"box": "TVBox"
},
"playerType": 0,
"type": 3
},
{
"key": "haiwaikan",
"name": "(js)海外看┃☕墙",
"api": "https://androidcatvodspider.netlify.app/json/js/haiwaikan.js",
"timeout": 30,
"ext": {
"box": "TVBox"
},
"playerType": 0,
"type": 3
},
{
"key": "dygangs",
"name": "(js)电影港┃🏖️",
"api": "https://androidcatvodspider.netlify.app/json/js/dygangs.js",
"timeout": 30,
"ext": {
"box": "TVBox"
},
"playerType": 0,
"type": 3
},
{
"key": "cilixiong",
"name": "(js)磁力熊┃🐻",
"api": "https://androidcatvodspider.netlify.app/json/js/cilixiong.js",
"timeout": 30,
"ext": {
"box": "TVBox"
},
"playerType": 0,
"type": 3
},
{
"key": "lovemovie",
"name": "(js)爱情电影网┃💕",
"api": "https://androidcatvodspider.netlify.app/json/js/lovemovie.js",
"timeout": 30,
"ext": {
"box": "TVBox"
},
"playerType": 0,
"type": 3
},
{
"key": "gitcafe",
"name": "(js)阿里纸条┃🦊",
"api": "https://androidcatvodspider.netlify.app/json/js/gitcafe.js",
"timeout": 30,
"ext": {
"box": "TVBox",
"aliToken": "26fc6787afff43e78b78992e782502f1",
"quarkCookie": "_UP_A4A_11_=wb965111521e45ffa80410c24a071a54; _UP_D_=pc; tfstk=fXFith4nnRk114LAjWc1TT-OQUXL5hGjqodxDjnVLDoBBchYujR4Drht3GaYxmqYlcPtWc34mknIMcFTB-Y_tuAv6G6_uIcxggIRw_U15jGV2EjCXmnslyoqlSMN9PGjgMEW0dR85uVOAjYmgwcEoqOqgIrqLyoIlq-ZuC738DgqgCJZgH8EuqxZNmAqqSPQTaC3h7bb2rFnSvW87D8jTW0iX0zasIR2zVDi4Poh2svabvzjnSTXixaaFogzbhS-Cry3xVcc9dlz--roR55Jj2wT8znUrEdYrfV3t-kh71znscDo-vYWpf24fSD_IE_78frQF0MNdMg367HmVvxFbyUnbY20XMOqX84UxYFpvQhbA-rqok-G4A9eUc4wG27YtK9jQ2gnVNJioG_mbu_h-wv5CAuIWgQh-K9jQ2gn2wbHFhMZRVIR.; __pus=c81f57897dafcb65d4ecb501bc299199AARcqF72zsatdbsCbiT3qVqsk36caaycoPQW7hz8rbEf+UY7f5aGgH1e90lsONAUwCAW8y27u5A/KXyYqkHCWgjS; __kp=99fa2760-1669-11ef-90cf-8f7a59c3b86e; __kps=AATSt4xuf6r6bqes3LdJvxvy; __ktd=c2e+aLICIvFoeklXXz36VA==; __uid=AATSt4xuf6r6bqes3LdJvxvy; Video-Auth=smob3MOUslklDq2MutANJYZCVo50sLv0GFelx3+cu1nK2fkdL2kvkdpT5yNOhNz0NLTyi5ThWRL47+ztJA4kXQ==; __puus=72f667c533c9a22496f88d2f1bb7ae71AAQ7mrvFw7s9AUPUXvnuGPkcDU3RRTVPdYaYQfsM9Cje2doYXgRZXbImg02EaUaEG+G9ikpo3xubGGdElArOuYvUtJzIXb6yHDnSZbtEUxkwvjfQRNEnDnVwLQ6LL2ORjRaxa9OUfwk/WppWvy6OcDqQtHYkaqB+Poxn5kFs7ZVdAtX7ZQks1czD+g9gAZjsbeBHxHQ1AP5MGc1s3M4RhwZQ"
},
"playerType": 0,
"type": 3
},
{
"key": "kuaikan",
"name": "(js)快看视频┃🛥︎",
"api": "https://androidcatvodspider.netlify.app/json/js/kuaikan.js",
"timeout": 30,
"ext": {
"box": "TVBox"
},
"playerType": 0,
"type": 3
} }
}
], ],
"parses": [ "parses": [
{ {

View File

@ -1,6 +1,7 @@
pluginManagement { pluginManagement {
repositories { repositories {
mavenCentral()
maven { url 'https://mirrors.huaweicloud.com/repository/maven/' }
maven { url 'https://maven.aliyun.com/repository/public/' } maven { url 'https://maven.aliyun.com/repository/public/' }
maven { url 'https://maven.aliyun.com/repository/google/' } maven { url 'https://maven.aliyun.com/repository/google/' }
maven { url 'https://maven.aliyun.com/repository/gradle-plugin/' } maven { url 'https://maven.aliyun.com/repository/gradle-plugin/' }
@ -9,6 +10,7 @@ pluginManagement {
maven { url 'https://maven.aliyun.com/repository/mapr-public/' } maven { url 'https://maven.aliyun.com/repository/mapr-public/' }
gradlePluginPortal() gradlePluginPortal()
google() google()
mavenCentral()
maven { url "https://plugins.gradle.org/m2/" } maven { url "https://plugins.gradle.org/m2/" }
maven { url "https://www.jitpack.io" } maven { url "https://www.jitpack.io" }
@ -17,7 +19,8 @@ pluginManagement {
dependencyResolutionManagement { dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories { repositories {
mavenCentral()
maven { url 'https://mirrors.huaweicloud.com/repository/maven/' }
maven { url 'https://maven.aliyun.com/repository/public/' } maven { url 'https://maven.aliyun.com/repository/public/' }
maven { url 'https://maven.aliyun.com/repository/google/' } maven { url 'https://maven.aliyun.com/repository/google/' }
maven { url 'https://maven.aliyun.com/repository/gradle-plugin/' } maven { url 'https://maven.aliyun.com/repository/gradle-plugin/' }
@ -25,6 +28,7 @@ dependencyResolutionManagement {
maven { url 'https://maven.aliyun.com/repository/central/'} maven { url 'https://maven.aliyun.com/repository/central/'}
maven { url 'https://maven.aliyun.com/repository/mapr-public/' } maven { url 'https://maven.aliyun.com/repository/mapr-public/' }
google() google()
mavenCentral()
maven { url "https://www.jitpack.io" } maven { url "https://www.jitpack.io" }
} }
} }