Compare commits

..

128 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
43 changed files with 2456 additions and 8470 deletions

View File

@ -1,27 +1,80 @@
name: Spider Jar Gen CI
on: workflow_dispatch
on:
push:
branches: [ "multiThread" ]
workflow_dispatch:
jobs:
build:
runs-on: windows-latest
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: set up JDK 21
uses: actions/setup-java@v2
with:
java-version: '21'
distribution: 'adopt'
cache: gradle
- name: Build with Gradle
run: ./build.bat ec
- name: Update spider jar
uses: EndBug/add-and-commit@v7
with:
default_author: github_actions
message: 'update spider jar'
add: "['./jar/custom_spider.jar', './jar/custom_spider.jar.md5']"
- uses: actions/checkout@v4
- name: Set up JDK 21
uses: actions/setup-java@v5
with:
java-version: '21'
distribution: 'jetbrains'
cache: gradle
- name: Grant execute permission to gradlew
run: chmod +x ./gradlew
- name: Build with Gradle
run: ./gradlew app:buildCustomSpiderJar
- name: Add & Commit
uses: EndBug/add-and-commit@v9.1.4
with:
default_author: github_actions
message: 'update spider jar and json'
add: "['./jar/custom_spider.jar', './jar/custom_spider.jar.md5','./json/index.json']"
- name: upload json
uses: SamKirkland/FTP-Deploy-Action@v4.3.6
with:
# ftp server
server: ftpupload.net
# ftp username
username: mseet_40633048
# ftp password
password: ${{ secrets.FTP_PWD }}
# Server port to connect to (read your web hosts docs)
port: 21
# protocol to deploy with - ftp, ftps, or ftps-legacy
protocol: ftp
# Folder to upload from, must end with trailing slash /
local-dir: ${{ github.workspace }}/json/
# Path to upload to on the server. Must end with trailing slash /
server-dir: htdocs/json/
# Deletes ALL contents of server-dir, even items in excluded with exclude argument
dangerous-clean-slate: true
exclude: |
**/js/**
- name: upload jar
uses: SamKirkland/FTP-Deploy-Action@v4.3.6
with:
# ftp server
server: ftpupload.net
# ftp username
username: mseet_40633048
# ftp password
password: ${{ secrets.FTP_PWD }}
# Server port to connect to (read your web hosts docs)
port: 21
# protocol to deploy with - ftp, ftps, or ftps-legacy
protocol: ftp
# Folder to upload from, must end with trailing slash /
local-dir: ${{ github.workspace }}/jar/
# Path to upload to on the server. Must end with trailing slash /
server-dir: htdocs/jar/
# Deletes ALL contents of server-dir, even items in excluded with exclude argument
dangerous-clean-slate: true

View File

@ -96,5 +96,128 @@ dependencies {
// implementation(ext: 'aar', name: 'quickjs', group: 'fongmi', version: 'release')
// api 'wang.harlon.quickjs:wrapper-android:2.0.0'
task buildCustomSpiderJar {
doLast {
def workDir = file("${projectDir}").parentFile
def spiderJarDir = file("$workDir/jar/spider.jar")
def smaliOutputDir = file("$workDir/jar/Smali_classes")
def customSpiderJar = file("$workDir/jar/custom_spider.jar")
def baksmaliJar = file("$workDir/jar/3rd/baksmali-2.5.2.jar")
def apktoolJar = file("$workDir/jar/3rd/apktool_2.4.1.jar")
def classesDex = file("$workDir/app/build/intermediates/dex/release/minifyReleaseWithR8/classes.dex")
def indexJsonFile = file("$workDir/json/index.json")
//
if (customSpiderJar.exists()) {
customSpiderJar.delete()
}
if (smaliOutputDir.exists()) {
delete(smaliOutputDir)
}
// 使 baksmali classes.dex
javaexec {
main = "-jar"
args = [
baksmaliJar.absolutePath,
"d",
classesDex.absolutePath,
"-o",
smaliOutputDir.absolutePath
]
}
// spider.jar
def targetDirs = [
file("$spiderJarDir/smali/com/github/catvod/spider"),
file("$spiderJarDir/smali/com/github/catvod/parser"),
file("$spiderJarDir/smali/com/github/catvod/js")
]
targetDirs.each { dir ->
if (dir.exists()) {
delete(dir)
}
}
//
def targetBaseDir = file("$spiderJarDir/smali/com/github/catvod")
if (!targetBaseDir.exists()) {
targetBaseDir.mkdirs()
}
//
def sourceDirs = [
file("$smaliOutputDir/com/github/catvod/spider"),
file("$smaliOutputDir/com/github/catvod/parser"),
file("$smaliOutputDir/com/github/catvod/js")
]
def targetPaths = [
file("$targetBaseDir/spider"),
file("$targetBaseDir/parser"),
file("$targetBaseDir/js")
]
sourceDirs.eachWithIndex { src, idx ->
if (src.exists()) {
copy {
from src
into targetPaths[idx]
}
delete(src)
}
}
// 使 apktool
javaexec {
main = "-jar"
args = [
apktoolJar.absolutePath,
"b",
spiderJarDir.absolutePath,
"-c"
]
}
// dex.jar custom_spider.jar
def distJar = file("$spiderJarDir/dist/dex.jar")
if (distJar.exists()) {
copy {
from distJar
into file("$workDir/jar")
rename "dex.jar", "custom_spider.jar"
}
}
// MD5
def md5File = file("$workDir/jar/custom_spider.jar.md5")
def md5 = java.security.MessageDigest.getInstance("MD5").digest(customSpiderJar.bytes).encodeHex().toString()
md5File.text = md5
// index.json md5
def indexJsonContent = indexJsonFile.text
// 使 spider MD5
def updatedContent = indexJsonContent.replaceAll(
/("spider":\s*"[^;]+;md5;)[^"]+"/,
'$1' + md5 + '"'
)
//
indexJsonFile.write(updatedContent)
//
delete(
"$spiderJarDir/build",
"$spiderJarDir/smali",
"$spiderJarDir/dist",
smaliOutputDir
)
}
}
tasks.named('buildCustomSpiderJar') {
dependsOn 'assembleRelease'
}
}

View File

@ -9,6 +9,7 @@ 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 {
@ -22,7 +23,8 @@ object BaiduDrive {
"Referer" to "https://pan.baidu.com/"
)
//是否删除过文件标志
private var deleteTag = 0;
private val saveDirName = "TVBOX_BD"
private var cookies = BaiDuYunHandler.get().token
@ -164,7 +166,8 @@ object BaiduDrive {
// 处理items
items.forEach { item ->
if (item.asJsonObject["isdir"].asInt == 1) {
val folderPath = "/sharelink$uk-$shareid/${item.asJsonObject["server_filename"].asString}"
val folderPath =
"/sharelink$uk-${item.asJsonObject["fs_id"].asString}/${item.asJsonObject["server_filename"].asString}"
if (folderPath !in seenFolders) {
seenFolders.add(folderPath)
pendingFolders.add(
@ -298,7 +301,7 @@ object BaiduDrive {
"shareid" to folderInfo["shareid"]!!.toString(),
"page" to folderInfo["page"].toString(),
"num" to "9999",
"dir" to folderInfo["dir"]!!.toString(),
"dir" to URLEncoder.encode(folderInfo["dir"]!!.toString()),
"desc" to "0",
"order" to "name",
)
@ -425,9 +428,12 @@ object BaiduDrive {
)
// 先清空文件夹在创建文件夹
_deleteTransferFile("/$saveDirName")
//创建路径
createSaveDir()
if (deleteTag == 0) {
_deleteTransferFile("/$saveDirName")
//创建路径
createSaveDir()
deleteTag = 1
}
val data =

View File

@ -9,6 +9,7 @@ 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
@ -50,7 +51,7 @@ object Pan123Api {
/**
* 登录方法
*/
fun login(passport: String, password: String): String? {
fun login(passport: String, password: String): JsonObject? {
val data = mapOf(
"passport" to passport, "password" to password, "remember" to true
@ -81,8 +82,9 @@ object Pan123Api {
val token = authData.getAsJsonObject("data").get("token").asString
// setAuth(token)
SpiderDebug.log("登录成功")
authToken=token
return token
return authData.get("data").asJsonObject
}
}
@ -119,7 +121,7 @@ object Pan123Api {
val queryPart = lurl.split("?")[1]
val pwdMatcher = Regex("[A-Za-z0-9]+").find(queryPart)
if (pwdMatcher != null) {
sharePwd = pwdMatcher.value
sharePwd = queryPart.split("=")[1]
}
}
@ -266,7 +268,9 @@ object Pan123Api {
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()}"
"Authorization" to "Bearer ${getAuth()}",
"platform" to "android"
)
try {

View File

@ -27,6 +27,7 @@ object Pan123Handler {
private var auth = ""
private var userName = ""
private var passwd = ""
private var expire = 0L;
fun getCache(): File {
return Path.tv("pan123")
@ -47,12 +48,12 @@ object Pan123Handler {
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)) {
SpiderDebug.log("userName passwd 不为空,auth 为空")
if (StringUtils.isBlank(auth) || expire == 0L || System.currentTimeMillis() > expire) {
SpiderDebug.log("auth 为空或者登录已过期")
this.loginWithPassword(userName, passwd)
}
} else {
SpiderDebug.log("userName passwd 为空")
startFlow()
@ -67,13 +68,14 @@ object Pan123Handler {
try {
//保存的账号密码
val auth = login(uname!!, passwd!!)
if (StringUtils.isNotBlank(auth)) {
val json = login(uname!!, passwd!!)
if (json != null) {
val user = User()
user.cookie = auth
user.cookie = json.get("token").asString
user.password = passwd
user.userName = uname
this.auth = auth ?: ""
user.expire = json.get("refresh_token_expire_time").asLong * 1000
this.auth = json.get("token").asString
cache?.setUserInfo(user)
Notify.show("123登录成功")
} else {

View File

@ -12,7 +12,6 @@ import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.ImageView;
import com.github.catvod.bean.Result;
import com.github.catvod.bean.Vod;
import com.github.catvod.bean.quark.Cache;
@ -24,30 +23,14 @@ import com.github.catvod.net.OkHttp;
import com.github.catvod.net.OkResult;
import com.github.catvod.spider.Init;
import com.github.catvod.spider.Proxy;
import com.github.catvod.utils.Json;
import com.github.catvod.utils.Notify;
import com.github.catvod.utils.Path;
import com.github.catvod.utils.ProxyServer;
import com.github.catvod.utils.ProxyVideo;
import com.github.catvod.utils.QRCode;
import com.github.catvod.utils.ResUtil;
import com.github.catvod.utils.Util;
import com.github.catvod.utils.*;
import com.google.gson.Gson;
import org.apache.commons.lang3.StringUtils;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import java.util.*;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
@ -227,15 +210,18 @@ public class QuarkApi {
String fileId = split[0], fileToken = split[1], shareId = split[2], stoken = split[3];
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();
header.remove("Host");
header.remove("Content-Type");
return Result.get().url(ProxyServer.INSTANCE.buildProxyUrl(playUrl, header)).octet().header(header).string();
if (flag.contains("quark原画")) {
playUrl = this.getDownload(shareId, stoken, fileId, fileToken, true);
return Result.get().url(ProxyServer.INSTANCE.buildProxyUrl(playUrl, header)).octet().header(header).string();
} else {
playUrl = this.getLiveTranscoding(shareId, stoken, fileId, fileToken, flag);
return Result.get().url(proxyVideoUrl(playUrl, header)).octet().header(header).string();
}
}
private String proxyVideoUrl(String url, Map<String, String> header) {
@ -428,7 +414,7 @@ public class QuarkApi {
for (Map<String, Object> item : items) {
if (Boolean.TRUE.equals(item.get("dir"))) {
subDir.add(item);
} else if (Boolean.TRUE.equals(item.get("file")) && "video".equals(item.get("obj_category"))) {
} else if (Boolean.TRUE.equals(item.get("file")) && (Util.isMedia((String) item.get("file_name")))) {
if ((Double) item.get("size") < 1024 * 1024 * 5) continue;
item.put("stoken", this.shareTokenCache.get(shareData.getShareId()).get("stoken"));
videos.add(Item.objectFrom(item, shareData.getShareId(), shareIndex));

View File

@ -23,7 +23,7 @@ import java.util.regex.Pattern;
public class TianyiApi {
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<>();
@ -221,41 +221,30 @@ public class TianyiApi {
public ShareData getShareData(String url, String accessCode) {
String shareCode = "";
// 第一种匹配规则使用预编译的 regex
Matcher matcher = Pattern.compile("https:\\/\\/cloud\\.189\\.cn\\/web\\/share\\?code=([^&]+)").matcher(url);
// 从整个URL中直接提取访问码无论格式如何
Matcher accessMatcher = Pattern.compile("访问码[:]([a-zA-Z0-9]+)").matcher(url);
if (accessMatcher.find()) {
accessCode = accessMatcher.group(1);
} else if (accessCode == null || accessCode.isEmpty()) {
accessCode = "";
}
String shareCode = null;
// 第一种匹配规则兼容http和https
Matcher matcher = Pattern.compile("https?:\\/\\/cloud\\.189\\.cn\\/web\\/share\\?code=([^&\\s]+)").matcher(url);
if (matcher.find() && matcher.group(1) != null) {
shareCode = matcher.group(1);
// 从shareCode中提取访问码
Matcher accessMatcher = Pattern.compile("访问码:([a-zA-Z0-9]+)").matcher(shareCode);
if (accessMatcher.find()) {
accessCode = accessMatcher.group(1);
} else {
accessCode = "";
}
} else {
// 第二种匹配规则直接匹配 cloud.189.cn/t/ 格式
Matcher fallbackMatcher = Pattern.compile("https://cloud\\.189\\.cn/t/([^&]+)").matcher(url);
// 第二种匹配规则直接匹配 cloud.189.cn/t/ 格式兼容http和https匹配到空格前
Matcher fallbackMatcher = Pattern.compile("https?:\\/\\/cloud\\.189\\.cn\\/t/([^\\s]+)").matcher(url);
if (fallbackMatcher.find()) {
shareCode = fallbackMatcher.group(1);
} 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.setSharePwd(accessCode);
return shareData;
}

View File

@ -44,7 +44,7 @@ public class UCApi {
private String cookieToken = "";
private String ckey = "";
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 Map<String, String> saveFileIdCaches = new HashMap<>();
private String saveDirId = null;
@ -87,7 +87,15 @@ public class UCApi {
cache = Cache.objectFrom(Path.read(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);
}
@ -181,9 +189,9 @@ public class UCApi {
List<String> playFrom = UCApi.get().getPlayFormatList();
List<String> playFromtmp = new ArrayList<>();
playFromtmp.add("uc原画");
for (String s : playFrom) {
/* for (String s : playFrom) {
playFromtmp.add("uc" + s);
}
}*/
List<String> playUrl = new ArrayList<>();
if (files.isEmpty()) {
@ -217,23 +225,19 @@ public class UCApi {
SpiderDebug.log("flag:" + flag);
String fileId = split[0], fileToken = split[1], shareId = split[2], stoken = split[3];
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);
Map<String, String> header = getHeaders();
header.remove("Host");
header.remove("Content-Type");
//UCTV 可以直接播放不需要代理
if (testVideo(playUrl)) {
SpiderDebug.log("UCTV 可以直接播放,不需要代理" );
if (flag.contains("uc原画")) {
playUrl = this.getDownload(shareId, stoken, fileId, fileToken, true);
return Result.get().url(playUrl).string();
} else {
playUrl = this.getLiveTranscoding(shareId, stoken, fileId, fileToken, flag);
return Result.get().url(proxyVideoUrl(playUrl, new HashMap<>())).string();
}
return Result.get().url(proxyVideoUrl(playUrl, header)).octet().header(header).string();
}
private boolean testVideo(String url) {

View File

@ -178,25 +178,7 @@ public class UCTokenHandler {
JsonObject resData = Json.safeObject(okResult.getBody());
String code = resData.get("code").getAsString();
pathname = "/token";
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);
OkResult okResult1 = getAccessToken(code, false);
if (okResult1.getCode() == 200) {
@ -208,15 +190,14 @@ public class UCTokenHandler {
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();
return result;
}
} else if (okResult.getCode() == 400) {
SpiderDebug.log("uc Token获取失败" +okResult.getBody());
SpiderDebug.log("uc Token获取失败" + okResult.getBody());
return Map.of("status", "NEW");
@ -226,6 +207,74 @@ public class UCTokenHandler {
platformStates.remove("UC_TOKEN");
return Map.of("status", "EXPIRED");
}
/**
* 获取访问令牌或者刷新令牌
*
* @param code
* @param refresh 是否刷新如果是code为refresh_token
* @return
* @throws UnsupportedEncodingException
*/
public OkResult getAccessToken(String code, boolean refresh) {
String timestamp = String.valueOf(new Date().getTime() / 1000 + 1) + "000";
String deviceID = StringUtils.isAllBlank((String) addition.get("DeviceID")) ? (String) addition.get("DeviceID") : generateDeviceID(timestamp);
Map<String, String> headers = new HashMap<>();
headers.put("Accept", "application/json, text/plain, */*");
headers.put("User-Agent", "Mozilla/5.0 (Linux; U; Android 13; zh-cn; M2004J7AC Build/UKQ1.231108.001) AppleWebKit/533.1 (KHTML, like Gecko) Mobile Safari/533.1");
String pathname = "/token";
String reqId = generateReqId(deviceID, timestamp);
Map<String, String> postData = new HashMap<>();
postData.put("req_id", reqId);
postData.put("app_ver", (String) conf.get("appVer"));
postData.put("device_id", deviceID);
postData.put("device_brand", "Xiaomi");
postData.put("platform", "tv");
postData.put("device_name", "M2004J7AC");
postData.put("device_model", "M2004J7AC");
postData.put("build_device", "M2004J7AC");
postData.put("build_product", "M2004J7AC");
postData.put("device_gpu", "Adreno (TM) 550");
try {
postData.put("activity_rect", URLEncoder.encode("{}", "UTF-8"));
} catch (Exception e) {
SpiderDebug.log("encode出错" + e.getMessage());
return null;
}
postData.put("channel", conf.get("channel"));
if (refresh) {
postData.put("refresh_token", code);
SpiderDebug.log("开始刷新uc accesstoken");
} else {
postData.put("code", code);
SpiderDebug.log("开始获取uc accesstoken");
}
return OkHttp.post(conf.get("codeApi") + pathname, Json.toJson(postData), headers);
}
/**
* 刷新refresh token
*
* @param refreshToken 刷新token
* @return 防火新的accesstoken
*/
public String refreshToken(String refreshToken) {
OkResult okResult1 = this.getAccessToken(refreshToken, true);
if (okResult1.getCode() == 200) {
JsonObject tokenResData = Json.safeObject(okResult1.getBody());
SpiderDebug.log("uc Token刷新成功" + tokenResData.get("data").getAsJsonObject().get("access_token").getAsString());
//保存到本地
cache.setTokenUser(User.objectFrom(Json.toJson(tokenResData.get("data").getAsJsonObject())));
return tokenResData.get("data").getAsJsonObject().get("access_token").getAsString();
}
return "";
}
public String download(String token, String saveFileId) throws Exception {
SpiderDebug.log("开始下载:" + saveFileId + ";token:" + token);
@ -266,6 +315,11 @@ public class UCTokenHandler {
OkResult okResult1 = OkHttp.get(API_URL + pathname, params, headers);
JsonObject obj = Json.safeObject(okResult1.getBody());
if (okResult1.getCode() != 200) {
Notify.show(obj.get("error_info").getAsString());
SpiderDebug.log("uc TV 错误信息:" + obj.get("error_info").getAsString());
return null;
}
String downloadUrl = obj.get("data").getAsJsonObject().get("video_info").getAsJsonArray().get(0).getAsJsonObject().get("url").getAsString();
SpiderDebug.log("uc TV 下载文件内容:" + downloadUrl);
return downloadUrl;

View File

@ -5,7 +5,8 @@ import com.google.gson.annotations.SerializedName;
public class User {
@SerializedName("expire")
private long expire;
@SerializedName("cookie")
private String cookie;
@ -38,6 +39,14 @@ public class User {
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;
@ -48,6 +57,7 @@ public class User {
this.cookie = "";
this.userName = "";
this.password = "";
this.expire = 0L;
}
}

View File

@ -64,7 +64,7 @@ class BaiDuPan : Spider() {
return TextUtils.join("$$$", BaiduDrive.get().getPlayFormatList());
}*/
for (i in 1..ids.size) {
playFrom.add("BD原画" + i + index)
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))
}*/
@ -78,7 +78,7 @@ class BaiDuPan : Spider() {
* @param ids share_link 集合
* @return 詳情內容視頻播放地址
*/
@Throws(Exception::class)
fun detailContentVodPlayUrl(ids: List<String>): String? {
val playUrl: MutableList<String?> = ArrayList<String?>()
for (id in ids) {
@ -86,6 +86,7 @@ class BaiDuPan : Spider() {
playUrl.add(getVod(id).getVodPlayUrl())
} catch (e: Exception) {
SpiderDebug.log("获取播放地址出错:" + e.message)
playUrl.add("")
}
}
return TextUtils.join("$$$", playUrl)

View File

@ -3,45 +3,48 @@ package com.github.catvod.spider;
import android.content.Context;
import android.text.TextUtils;
import com.github.catvod.api.Pan123Api;
import com.github.catvod.api.TianyiApi;
import com.github.catvod.crawler.Spider;
import com.github.catvod.crawler.SpiderDebug;
import com.github.catvod.utils.Json;
import com.github.catvod.utils.Util;
import com.google.gson.JsonObject;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.ImmutablePair;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import static com.github.catvod.api.TianyiApi.URL_START;
import static com.github.catvod.api.TianyiApi.URL_CONTAIN;
/**
* @author ColaMint & Adam & FongMi
*/
public class Cloud extends Spider {
private Quark quark = null;
/* private Ali ali = null;
private UC uc = null;*/
/* private Ali ali = null;*/
private UC uc = null;
private TianYi tianYi = null;
private YiDongYun yiDongYun = null;
private BaiDuPan baiDuPan = null;
private Pan123 pan123 = null;
private static final Map<String, ImmutablePair<List<String>, List<String>>> resultMap = new HashMap<>();
@Override
public void init(Context context, String extend) throws Exception {
JsonObject ext = Json.safeObject(extend);
quark = new Quark();
/* uc = new UC();
ali = new Ali();*/
uc = new UC();
/* ali = new Ali();*/
tianYi = new TianYi();
yiDongYun = new YiDongYun();
baiDuPan = new BaiDuPan();
pan123 = new Pan123();
boolean first = Objects.nonNull(ext);
quark.init(context, first && ext.has("cookie") ? ext.get("cookie").getAsString() : "");
/* uc.init(context, first && ext.has("uccookie") ? ext.get("uccookie").getAsString() : "");
ali.init(context, first && ext.has("token") ? ext.get("token").getAsString() : "");*/
uc.init(context, first && ext.has("uccookie") ? ext.get("uccookie").getAsString() : "");
/* ali.init(context, first && ext.has("token") ? ext.get("token").getAsString() : "");*/
tianYi.init(context, first && ext.has("tianyicookie") ? ext.get("tianyicookie").getAsString() : "");
yiDongYun.init(context, "");
baiDuPan.init(context, "");
@ -58,9 +61,9 @@ public class Cloud extends Spider {
} else */
if (shareUrl.get(0).matches(Util.patternQuark)) {
return quark.detailContent(shareUrl);
} /*else if (shareUrl.get(0).matches(Util.patternUC)) {
} else if (shareUrl.get(0).matches(Util.patternUC)) {
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);
} else if (shareUrl.get(0).contains(YiDongYun.URL_START)) {
return yiDongYun.detailContent(shareUrl);
@ -79,9 +82,9 @@ public class Cloud extends Spider {
if (flag.contains("quark")) {
return quark.playerContent(flag, id, vipFlags);
} /*else if (flag.contains("uc")) {
} else if (flag.contains("uc")) {
return uc.playerContent(flag, id, vipFlags);
} */ else if (flag.contains("天意")) {
} else if (flag.contains("天意")) {
return tianYi.playerContent(flag, id, vipFlags);
} else if (flag.contains("移动")) {
return yiDongYun.playerContent(flag, id, vipFlags);
@ -96,51 +99,94 @@ public class Cloud extends Spider {
}
protected String detailContentVodPlayFrom(List<String> shareLinks) {
List<String> from = new ArrayList<>();
int i = 0;
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));
} else if (shareLink.contains(BaiDuPan.URL_START)) {
from.add(baiDuPan.detailContentVodPlayFrom(List.of(shareLink), i));
} else if (shareLink.matches(Pan123Api.regex)) {
from.add(pan123.detailContentVodPlayFrom(List.of(shareLink), i));
}
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);
}
return TextUtils.join("$$$", from);
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) throws Exception {
List<String> urls = new ArrayList<>();
for (String shareLink : shareLinks) {
/* if (shareLink.matches(Util.patternUC)) {
urls.add(uc.detailContentVodPlayUrl(List.of(shareLink)));
} else */
if (shareLink.matches(Util.patternQuark)) {
urls.add(quark.detailContentVodPlayUrl(List.of(shareLink)));
}/* else if (shareLink.matches(Util.patternAli)) {
urls.add(ali.detailContentVodPlayUrl(List.of(shareLink)));
} */ else if (shareLink.startsWith(URL_START)) {
urls.add(tianYi.detailContentVodPlayUrl(List.of(shareLink)));
} else if (shareLink.contains(YiDongYun.URL_START)) {
urls.add(yiDongYun.detailContentVodPlayUrl(List.of(shareLink)));
} else if (shareLink.contains(BaiDuPan.URL_START)) {
urls.add(baiDuPan.detailContentVodPlayUrl(List.of(shareLink)));
} else if (shareLink.matches(Pan123Api.regex)) {
urls.add(pan123.detailContentVodPlayUrl(List.of(shareLink)));
}
protected String detailContentVodPlayUrl(List<String> shareLinks) {
ImmutablePair<List<String>, List<String>> pairs = resultMap.get(Util.MD5(Json.toJson(shareLinks)));
if (pairs != null && pairs.left != null && !pairs.left.isEmpty()) {
return TextUtils.join("$$$", pairs.left);
}
getPlayFromAndUrl(shareLinks);
pairs = resultMap.get(Util.MD5(Json.toJson(shareLinks)));
if (pairs != null && pairs.left != null && !pairs.left.isEmpty()) {
return TextUtils.join("$$$", pairs.left);
}
return "";
}
//同時获取from 和url 放入缓存只要一个函数执行就行避免重复执行
private void getPlayFromAndUrl(List<String> shareLinks) {
ExecutorService service = Executors.newFixedThreadPool(4);
try { //首先清空缓存避免太多缓存
resultMap.clear();
List<String> urls = new ArrayList<>();
List<String> froms = new ArrayList<>();
List<Future<ImmutablePair<String, String>>> futures = new ArrayList<>();
int i = 0;
for (String shareLink : shareLinks) {
int finalI = ++i;
futures.add(service.submit(() -> {
String url = "";
String from = "";
if (shareLink.matches(Util.patternUC)) {
url = uc.detailContentVodPlayUrl(List.of(shareLink));
from = uc.detailContentVodPlayFrom(List.of(shareLink), finalI);
} else if (shareLink.matches(Util.patternQuark)) {
url = quark.detailContentVodPlayUrl(List.of(shareLink));
from = quark.detailContentVodPlayFrom(List.of(shareLink), finalI);
}/* else if (shareLink.matches(Util.patternAli)) {
urls.add(ali.detailContentVodPlayUrl(List.of(shareLink)));
} */ else if (shareLink.contains(URL_CONTAIN)) {
url = tianYi.detailContentVodPlayUrl(List.of(shareLink));
from = tianYi.detailContentVodPlayFrom(List.of(shareLink), finalI);
} else if (shareLink.contains(YiDongYun.URL_START)) {
url = yiDongYun.detailContentVodPlayUrl(List.of(shareLink));
from = yiDongYun.detailContentVodPlayFrom(List.of(shareLink), finalI);
} else if (shareLink.contains(BaiDuPan.URL_START)) {
url = baiDuPan.detailContentVodPlayUrl(List.of(shareLink));
from = baiDuPan.detailContentVodPlayFrom(List.of(shareLink), finalI);
} else if (shareLink.matches(Pan123Api.regex)) {
url = pan123.detailContentVodPlayUrl(List.of(shareLink));
from = pan123.detailContentVodPlayFrom(List.of(shareLink), finalI);
}
return new ImmutablePair<>(url, from);
}));
}
for (Future<ImmutablePair<String, String>> future : futures) {
//只有连接不为空才放入进去
if (StringUtils.isNoneBlank(future.get().left)) {
urls.add(future.get().left);
froms.add(future.get().right);
}
}
resultMap.put(Util.MD5(Json.toJson(shareLinks)), new ImmutablePair<>(urls, froms));
SpiderDebug.log("---urls" + Json.toJson(urls));
SpiderDebug.log("---froms" + Json.toJson(froms));
} catch (Exception e) {
SpiderDebug.log("获取异步结果出错:" + e);
} finally {
service.shutdown();
}
return TextUtils.join("$$$", urls);
}
}

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

@ -58,7 +58,7 @@ public class Pan123 extends Spider {
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(String.format(Locale.getDefault(), "pan123" + s + "#%02d_%02d", i, index));
}
// playFrom.add("天意" + i + index);
@ -72,7 +72,7 @@ public class Pan123 extends Spider {
* @param ids share_link 集合
* @return 詳情內容視頻播放地址
*/
public String detailContentVodPlayUrl(List<String> ids) throws Exception {
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);
@ -80,6 +80,7 @@ public class Pan123 extends Spider {
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

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

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,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

@ -3,8 +3,8 @@ 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 org.jsoup.Jsoup
import java.net.URLEncoder
import java.nio.charset.Charset
@ -12,7 +12,7 @@ import java.nio.charset.Charset
* @author zhixc
*/
class Tg189Search : Cloud() {
private val URL = "https://tg.252035.xyz/"
private val URL = "https://tgsou.252035.xyz/"
private val header: Map<String, String>
get() {
@ -30,16 +30,24 @@ class Tg189Search : Cloud() {
)
val list: MutableList<Vod> = ArrayList()
val html = OkHttp.string(url, header)
val arr = html.split(":I".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
if (arr.isNotEmpty()) {
for (s in arr) {
val doc = Jsoup.parse(s)
val id = doc.select(" a").eachAttr("href")
.first { it.contains("189") || it.contains("139") }
val name=doc.select("strong").text()
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, ""))
}
list.add(Vod(id, name, "", ""))
}
}

View File

@ -3,8 +3,8 @@ 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 org.jsoup.Jsoup
import java.net.URLEncoder
import java.nio.charset.Charset
@ -12,12 +12,11 @@ import java.nio.charset.Charset
* @author zhixc
*/
class TgQuarkSearch : Cloud() {
private val URL = "https://tg.252035.xyz/"
private val URL = "https://tgsou.252035.xyz/"
private val header: Map<String, String>
get() {
val header: MutableMap<String, String> =
HashMap()
val header: MutableMap<String, String> = HashMap()
header["User-Agent"] = Util.CHROME
return header
}
@ -27,23 +26,32 @@ class TgQuarkSearch : Cloud() {
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()
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, "", ""))
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,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 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 (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);
@ -69,7 +69,7 @@ public class TianYi extends Spider {
* @param ids share_link 集合
* @return 詳情內容視頻播放地址
*/
public String detailContentVodPlayUrl(List<String> ids) throws Exception {
public String detailContentVodPlayUrl(List<String> ids) {
List<String> playUrl = new ArrayList<>();
for (String id : ids) {
ShareData shareData = TianyiApi.get().getShareData(id, "");
@ -77,6 +77,7 @@ public class TianYi extends Spider {
playUrl.add(TianyiApi.get().getVod(shareData).getVodPlayUrl());
} catch (Exception e) {
SpiderDebug.log("获取播放地址出错:" + e.getMessage());
playUrl.add("");
}
}
return TextUtils.join("$$$", playUrl);

View File

@ -56,10 +56,10 @@ public class UC extends Spider {
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("uc原画" + i + index);
}
return TextUtils.join("$$$", playFrom);
@ -71,11 +71,17 @@ public class UC extends Spider {
* @param ids share_link 集合
* @return 詳情內容視頻播放地址
*/
public String detailContentVodPlayUrl(List<String> ids) throws Exception {
public String detailContentVodPlayUrl(List<String> ids) {
List<String> playUrl = new ArrayList<>();
for (String id : ids) {
ShareData shareData = UCApi.get().getShareData(id);
playUrl.add(UCApi.get().getVod(shareData).getVodPlayUrl());
try {
ShareData shareData = UCApi.get().getShareData(id);
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);
}

View File

@ -2,13 +2,11 @@ package com.github.catvod.spider;
import android.content.Context;
import android.text.TextUtils;
import com.github.catvod.api.YunDrive;
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 org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
@ -96,8 +94,8 @@ public class YiDongYun extends Spider {
int i = 0;
for (String id : ids) {
i++;
playFrom.add("移动(极速)" + i);
playFrom.add("移动(原画)" + i);
playFrom.add(String.format("移动(极速)#%2d", i));
playFrom.add(String.format("移动(原画)#%2d", i));
}
@ -111,10 +109,17 @@ public class YiDongYun extends Spider {
* @param ids share_link 集合
* @return 詳情內容視頻播放地址
*/
public String detailContentVodPlayUrl(List<String> ids) throws Exception {
public String detailContentVodPlayUrl(List<String> ids) {
List<String> playUrl = new ArrayList<>();
for (String id : ids) {
playUrl.add(getVod(List.of(id)).getVodPlayUrl());
try {
playUrl.add(getVod(List.of(id)).getVodPlayUrl());
}catch (Exception e) {
SpiderDebug.log("获取播放地址出错:" + e.getMessage());
playUrl.add("");
}
}
return TextUtils.join("$$$", playUrl);
}

View File

@ -8,11 +8,12 @@ import kotlinx.coroutines.Job
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import java.io.ByteArrayInputStream
object ProxyServer {
private const val THREAD_NUM = 16
private val partSize = 1024 * 1024 * 2
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>>>();
@ -37,15 +38,20 @@ object ProxyServer {
}
};
httpServer?.addRoutes("/proxy") { req, response ->
run {
val key = req.queryParams["key"];
val url = urlMap[key]
val header = headerMap[key]
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)
if (url != null && header != null) {
proxyAsync(url, header, req, response)
}
}
} catch (e: Exception) {
SpiderDebug.log("代理视频出错:" + e.message)
}
}
httpServer?.start()
@ -138,7 +144,13 @@ object ProxyServer {
producerJob += CoroutineScope(Dispatchers.IO).launch {
// 异步下载数据块
val data = getVideoStream(chunkStart, chunkEnd, url, headers)
channels[i].send(data)
//如果是0开始且检测到恶意头那么就把数据截断
if (chunkStart == 0L) {
val offset = detectMaliciousPrefix(data)
channels[i].send(data.copyOfRange(offset, data.size))
} else {
channels[i].send(data)
}
}
currentStart = chunkEnd + 1
@ -164,6 +176,64 @@ object ProxyServer {
}
}
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

View File

@ -23,6 +23,8 @@ import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static android.net.Uri.encode;
public class Util {
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\\/[^\"]+)";
@ -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 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 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 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");
@ -257,6 +261,12 @@ public class Util {
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) {
return new String(android.util.Base64.decode(s, Base64.NO_WRAP), Charset.defaultCharset());

View File

@ -28,7 +28,7 @@ public class QuarkTest {
Init.init(mockContext);
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, "_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();
}
@ -41,7 +41,7 @@ public class QuarkTest {
@org.junit.Test
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);
JsonObject map = Json.safeObject(content);
Gson gson = new GsonBuilder().setPrettyPrinting().create();
@ -52,7 +52,7 @@ public class QuarkTest {
@org.junit.Test
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);
JsonObject map = Json.safeObject(content);
Gson gson = new GsonBuilder().setPrettyPrinting().create();

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

@ -15,7 +15,7 @@ class BaiduDriveTest {
runBlocking {
val reslut =
BaiduDrive.processShareLinks(listOf("https://pan.baidu.com/s/1Ov0S6S7rqnyW_S3AondhmQ?pwd=8888"))
BaiduDrive.processShareLinks(listOf("https://pan.baidu.com/s/1So5RhSmNts0rWKEzjqinhQ?pwd=9527"))
System.out.println(Json.toJson(reslut))
}
}
@ -51,7 +51,7 @@ class BaiduDriveTest {
System.out.println(reslut)
}*/
val jsonStr =
com.github.catvod.utils.Util.base64Decode("eyJ1ayI6IjExMDMyNzkxMjIzNDEiLCJzaGFyZWlkIjoiMjk1NzE4ODcyOTgiLCJmaWQiOjMxOTM5NTUxMTQyOTU4MCwicmFuZHNrIjoidkd4WXh4TVBRcXpucXZialRQeUQ2Q1FFT2VqemtJWmdFdXV2OUQ1Y3R6TSUzRCIsInBuYW1lIjoiRHJhZ29uIEJhbGwgREFJTUEuUzAxRTAxLjIwMjQuMTA4MHAuQ1IuV0VCLURMLngyNjQuQUFDLm1rdiIsInF0eXBlIjoib3JpZ2luYWwifQ==")
com.github.catvod.utils.Util.base64Decode("eyJ1ayI6IjI0MDAxMjE2NzIiLCJzaGFyZWlkIjoiMjc2NTk2OTA4MTAiLCJmaWQiOjcxNzUwMDM4OTg1MjYzOSwicmFuZHNrIjoiNEdjMzFTejVsZHNpdHcwRW12ZDNzam9XYWFuVjFEQlFsUHk3VkdESHklMkI0JTNEIiwicG5hbWUiOiJUaGUuUmV0dXJuLm9mLnRoZS5MYW1lLkhlcm8uMjAyNS4yMTYwcC5XRUItREwuSDI2NS5IRFIuNjBmcHMuRERQNS4xLURyZWFtSEQubWt2IiwicXR5cGUiOiJvcmlnaW5hbCJ9")
val obj = Json.safeObject(jsonStr)
runBlocking {
val reslut = BaiduDrive.getVideoUrl(obj, "BD原画1")

View File

@ -21,9 +21,9 @@ public class TianyiApiTest {
@Test
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);
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);
ShareData shareData2 = TianyiApi.get().getShareData("https://cloud.189.cn/t/ZvEjUvq6FNr2", "");

View File

@ -18,7 +18,7 @@ public class ProxyVideoTest {
// "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("https://media.w3.org/2010/05/sintel/trailer.mp4", Map.of("header","2","header2","2")));
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) {

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -1 +1 @@
77ff0d77d6fccecddd174ee82161f3bd
5cca7cdb874fb79dee68e08a876fc8ec

View File

@ -1,5 +1,5 @@
{
"spider": "https://andoridspidermt.netlify.app/jar/custom_spider.jar;md5;53bfcd21daf6443ac9c0922ef11a640c",
"spider": "http://gh.halonice.com/https://raw.githubusercontent.com/lushunming/AndroidCatVodSpider/refs/heads/multiThread/jar/custom_spider.jar;md5;5cca7cdb874fb79dee68e08a876fc8ec",
"lives": [
{
"name": "电视直播",
@ -66,6 +66,15 @@
"changeable": 1,
"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",
"name": "盘他|139Pan",
@ -94,32 +103,69 @@
"ext": {}
},
{
"key": "TgSearch",
"name": "TgSearch|Pan",
"key": "Qupans",
"name": "Qupans|Pan",
"type": 3,
"api": "csp_TgSearch",
"api": "csp_Qupans",
"searchable": 1,
"changeable": 1,
"ext": {}
},
{
"key": "TgQuarkSearch",
"name": "TgQuarkSearch|Pan",
"key": "Tg123Search",
"name": "Tg123Search",
"type": 3,
"api": "csp_TgQuarkSearch",
"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|Pan",
"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┃网盘",
@ -129,12 +175,69 @@
"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": [
"移动"
]
}
},
@ -354,7 +457,34 @@
"api": "csp_TianYiSo",
"searchable": 1,
"timeout": 30
},
{
"key": "local",
"name": "Local",
"type": 3,
"api": "csp_Local",
"searchable": 0,
"changeable": 0
},
{
"key": "market",
"name": "Market",
"type": 3,
"api": "csp_Market",
"searchable": 0,
"changeable": 0,
"ext": "./market.json"
},
{
"key": "push_agent",
"name": "Push",
"type": 3,
"api": "csp_Push",
"searchable": 0,
"changeable": 0,
"timeout": 60
}
],
"parses": [
{