Compare commits

..

No commits in common. "multiThread" and "mine" have entirely different histories.

87 changed files with 9485 additions and 7691 deletions

View File

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

View File

@ -2,7 +2,6 @@ plugins {
id 'com.android.application'
id 'ru.cleverpumpkin.proguard-dictionaries-generator'
id 'org.jetbrains.kotlin.android'
}
java {
toolchain {
@ -48,12 +47,6 @@ android {
jvmTarget = "11"
}
packagingOptions {
exclude 'META-INF/*'
}
configurations.configureEach {
resolutionStrategy {
@ -85,139 +78,9 @@ dependencies {
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.1"
// https://mvnrepository.com/artifact/com.sun.net.httpserver/http
/* implementation(files("libs/sun-common-server.jar"))
implementation("com.sun.net.httpserver:http:20070405")*/
// implementation("com.hibegin:simplewebserver:0.1.15")
//implementation("com.github.codeborne.klite:klite-server:1.7.0")
//implementation 'wang.harlon.quickjs:wrapper-java:1.0.0'
// implementation(ext: 'aar', name: 'quickjs', group: 'fongmi', version: 'release')
// api 'wang.harlon.quickjs:wrapper-android:2.0.0'
task buildCustomSpiderJar {
doLast {
def workDir = file("${projectDir}").parentFile
def spiderJarDir = file("$workDir/jar/spider.jar")
def smaliOutputDir = file("$workDir/jar/Smali_classes")
def customSpiderJar = file("$workDir/jar/custom_spider.jar")
def baksmaliJar = file("$workDir/jar/3rd/baksmali-2.5.2.jar")
def apktoolJar = file("$workDir/jar/3rd/apktool_2.4.1.jar")
def classesDex = file("$workDir/app/build/intermediates/dex/release/minifyReleaseWithR8/classes.dex")
def indexJsonFile = file("$workDir/json/index.json")
//
if (customSpiderJar.exists()) {
customSpiderJar.delete()
}
if (smaliOutputDir.exists()) {
delete(smaliOutputDir)
}
// 使 baksmali classes.dex
javaexec {
main = "-jar"
args = [
baksmaliJar.absolutePath,
"d",
classesDex.absolutePath,
"-o",
smaliOutputDir.absolutePath
]
}
// spider.jar
def targetDirs = [
file("$spiderJarDir/smali/com/github/catvod/spider"),
file("$spiderJarDir/smali/com/github/catvod/parser"),
file("$spiderJarDir/smali/com/github/catvod/js")
]
targetDirs.each { dir ->
if (dir.exists()) {
delete(dir)
}
}
//
def targetBaseDir = file("$spiderJarDir/smali/com/github/catvod")
if (!targetBaseDir.exists()) {
targetBaseDir.mkdirs()
}
//
def sourceDirs = [
file("$smaliOutputDir/com/github/catvod/spider"),
file("$smaliOutputDir/com/github/catvod/parser"),
file("$smaliOutputDir/com/github/catvod/js")
]
def targetPaths = [
file("$targetBaseDir/spider"),
file("$targetBaseDir/parser"),
file("$targetBaseDir/js")
]
sourceDirs.eachWithIndex { src, idx ->
if (src.exists()) {
copy {
from src
into targetPaths[idx]
}
delete(src)
}
}
// 使 apktool
javaexec {
main = "-jar"
args = [
apktoolJar.absolutePath,
"b",
spiderJarDir.absolutePath,
"-c"
]
}
// dex.jar custom_spider.jar
def distJar = file("$spiderJarDir/dist/dex.jar")
if (distJar.exists()) {
copy {
from distJar
into file("$workDir/jar")
rename "dex.jar", "custom_spider.jar"
}
}
// MD5
def md5File = file("$workDir/jar/custom_spider.jar.md5")
def md5 = java.security.MessageDigest.getInstance("MD5").digest(customSpiderJar.bytes).encodeHex().toString()
md5File.text = md5
// index.json md5
def indexJsonContent = indexJsonFile.text
// 使 spider MD5
def updatedContent = indexJsonContent.replaceAll(
/("spider":\s*"[^;]+;md5;)[^"]+"/,
'$1' + md5 + '"'
)
//
indexJsonFile.write(updatedContent)
//
delete(
"$spiderJarDir/build",
"$spiderJarDir/smali",
"$spiderJarDir/dist",
smaliOutputDir
)
}
}
tasks.named('buildCustomSpiderJar') {
dependsOn 'assembleRelease'
}
}

Binary file not shown.

View File

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

View File

@ -507,7 +507,7 @@ public class AliYun {
Map<String, String> headers = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
List<String> keys = Arrays.asList("referer", "icy-metadata", "range", "connection", "accept-encoding", "user-agent");
for (String key : params.keySet()) if (keys.contains(key)) headers.put(key, params.get(key));
return ProxyVideo.proxyMultiThread(downloadUrl, headers);
return ProxyVideo.proxy(downloadUrl, headers);
}
private String getM3u8Url(String shareId, String fileId, String templateId) {

View File

@ -1,262 +0,0 @@
package com.github.catvod.api
import android.app.AlertDialog
import android.content.DialogInterface
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.text.TextUtils
import android.view.Gravity
import android.widget.FrameLayout
import android.widget.ImageView
import com.github.catvod.bean.BD.Cache
import com.github.catvod.bean.yun.User
import com.github.catvod.crawler.SpiderDebug
import com.github.catvod.net.OkHttp
import com.github.catvod.spider.Init
import com.github.catvod.utils.*
import okhttp3.Headers
import okhttp3.Request
import org.apache.commons.lang3.StringEscapeUtils
import java.io.File
import java.io.IOException
import java.io.UnsupportedEncodingException
import java.util.concurrent.Executors
import java.util.concurrent.ScheduledExecutorService
import java.util.concurrent.TimeUnit
import kotlin.concurrent.Volatile
class BaiDuYunHandler private constructor() {
private val cache: Cache
private var service: ScheduledExecutorService? = null
private var dialog: AlertDialog? = null
private var cookies = ""
private val headers = mapOf(
"User-Agent" to "Mozilla/5.0 (Linux; Android 12; SM-X800) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.40 Safari/537.36",
"Accept" to "application/json, text/plain, */*",
"Content-Type" to "application/x-www-form-urlencoded",
"Origin" to "https://pan.baidu.com",
"Referer" to "https://pan.baidu.com/"
)
fun getCache(): File {
return Path.tv("bd")
}
init {
cache = Cache.objectFrom(Path.read(getCache()))
}
private object Loader {
@Volatile
var INSTANCE: BaiDuYunHandler = BaiDuYunHandler()
}
val token: String
get() {
val user: User = cache.getUser()
return user.getCookie()
//return "cGM6MTg4OTY3ODE2MDE6eTM1Tjd1dG58MXxSQ1N8MTc1NDQ2OTgwNzEyOXxzMlN0T1VEV3lOVmF5V3pNbGFfM2tJbVp1ZmlqSHBqaEhTSzVyNHZqVXNRLmlhV3loSUxHNDFkMUI5N1BqXzhWN0dtVWtKLnBTclhpNGpZU1EuTGZWMTV3MVFoZmNpcEVoZkxUV2tvYjB0bkFTYV9RTUhhaHhveWx6YkdmcEhQdjNCS1lrbnp1LkxaWDdKOE40YkNNRjkzT3piNmx2Y0d3TWdVUkl5b18ubVUt";
}
companion object {
@JvmStatic
fun get(): BaiDuYunHandler {
return Loader.INSTANCE
}
}
@Throws(java.lang.Exception::class)
fun startScan(): ByteArray {
val result = loginByQRCode()
// Step 2: Get QR Code
val byteStr: ByteArray = downloadQRCode(result["qrCodeImageUrl"]!!);
Init.run(Runnable { showQRCode(byteStr) })
// Step 3: Check login status
Init.execute(Runnable {
startService(
result["sign"]!!
)
})
return byteStr
}
@Throws(IOException::class)
fun downloadQRCode(url: String): ByteArray {
val headers: MutableMap<String?, String?> = HashMap<String?, String?>()
headers.put(
"user-agent",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36"
)
val request = Request.Builder().url(url).headers(Headers.of(headers)).build()
val response = OkHttp.newCall(request)
if (response.code() == 200) {
return response.body()!!.bytes()
}
return "".toByteArray()
}
fun loginByQRCode(): Map<String, String> {
return try {
// 获取登录二维码
val timestamp = System.currentTimeMillis()
val qrCodeUrl = "https://passport.baidu.com/v2/api/getqrcode?lp=pc&_=$timestamp"
val response = OkHttp.string(qrCodeUrl, emptyMap(), headers)
val json = Json.safeObject(response)
if (json["errno"].asInt != 0) {
return mapOf("error" to "获取登录二维码错误, code: ${json["errno"].asInt}")
}
val sign = json["sign"].asString
val imgurl = json["imgurl"].asString
val qrCodeImageUrl = "https://$imgurl"
val qrLoginUrl =
"https://wappass.baidu.com/wp/?qrlogin&t=$timestamp" + "&error=0&sign=$sign&cmd=login&lp=pc&tpl=netdisk&uaonly=" + "&client_id=&adapter=3&client=&qrloginfrom=pc&wechat=0&traceid="
// 返回二维码信息供前端显示
val result = mapOf(
"qrCodeImageUrl" to qrCodeImageUrl, "qrLoginUrl" to qrLoginUrl, "sign" to sign
)
result
} catch (e: Exception) {
mapOf("error" to "获取二维码失败: ${e.message}")
}
}
fun checkQRLoginStatus(sign: String): Map<String, Any> {
return try {
val timestamp = System.currentTimeMillis()
val checkUrl =
"https://passport.baidu.com/channel/unicast?channel_id=$sign" + "&tpl=netdisk&callback=&apiver=v3&tt=$timestamp&_=$timestamp"
val response = OkHttp.string(checkUrl, emptyMap(), headers)
val cleanResponse = response.trim('(', ' ', '\n', ')')
val json = Json.safeObject(cleanResponse)
if (json["errno"].asInt == 0) {
SpiderDebug.log("百度扫码成功")
val channelV = json["channel_v"].asString
val channelJson = Json.safeObject(channelV)
if (channelJson["status"].asInt == 0) {
val bduss = channelJson["v"].asString
// 执行登录
val loginTimestamp = System.currentTimeMillis()
val loginUrl =
"https://passport.baidu.com/v3/login/main/qrbdusslogin?" + "v=$loginTimestamp&bduss=$bduss&u=&loginVersion=v4&qrcode=1&tpl=netdisk&apiver=v3" + "&tt=$loginTimestamp&traceid=&callback=bd__cbs__cupstt"
val loginResponse = OkHttp.get(loginUrl, emptyMap(), headers)
val cleanLoginResponse = loginResponse.body.substringAfter("(").substringBeforeLast(")")
val loginJson = Json.safeObject(StringEscapeUtils.unescapeHtml4(cleanLoginResponse))
if (loginJson.has("errInfo") && loginJson["errInfo"].asJsonObject["no"].asString == "0") {
// 登录成功设置cookie
SpiderDebug.log("百度登录成功设置cookie${bduss}")
cookies = "BDUSS=$bduss"
cookies = generateCooike(loginResponse.resp["set-cookie"])
if (cookies.isNotEmpty()) {
cache.setUser(User.objectFrom(this.cookies))
//停止检验线程,关闭弹窗
stopService()
Notify.show("百度登录成功")
}
mapOf("success" to true, "bduss" to bduss)
} else {
mapOf("error" to "登录失败: $cleanLoginResponse")
}
} else {
mapOf("status" to channelJson["status"].asInt)
}
} else {
mapOf("errno" to json["errno"].asInt)
}
} catch (e: Exception) {
mapOf("error" to "检查登录状态失败: ${e.message}")
}
}
fun generateCooike(cookies: List<String>?): String {
if (cookies == null || cookies.isEmpty()) {
return ""
}
val cookieList: MutableList<String?> = ArrayList<String?>()
for (cookie in cookies) {
cookieList.add(cookie.split(";".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()[0])
}
return TextUtils.join(";", cookieList)
}
/**
* 显示qrcode
*
* @param base64Str
*/
fun showQRCode(bytes: ByteArray) {
try {
val size = ResUtil.dp2px(240)
val params = FrameLayout.LayoutParams(size, size)
val image = ImageView(Init.context())
image.setScaleType(ImageView.ScaleType.CENTER_CROP)
image.setImageBitmap(QRCode.Bytes2Bimap(bytes))
val frame = FrameLayout(Init.context())
params.gravity = Gravity.CENTER
frame.addView(image, params)
dialog = AlertDialog.Builder(Init.getActivity()).setView(frame)
.setOnCancelListener(DialogInterface.OnCancelListener { dialog: DialogInterface? -> this.dismiss(dialog) })
.setOnDismissListener(DialogInterface.OnDismissListener { dialog: DialogInterface? ->
this.dismiss(dialog)
}).show()
dialog!!.getWindow()!!.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
Notify.show("请使用百度网盘App扫描二维码")
} catch (ignored: java.lang.Exception) {
}
}
private fun dismiss() {
try {
dialog?.dismiss()
} catch (ignored: java.lang.Exception) {
}
}
private fun dismiss(dialog: DialogInterface?) {
stopService()
}
private fun stopService() {
service?.shutdownNow()
Init.run(Runnable { this.dismiss() })
}
fun startService(sign: String) {
SpiderDebug.log("----start 百度 token service")
service = Executors.newScheduledThreadPool(1)
service?.scheduleWithFixedDelay(Runnable {
try {
SpiderDebug.log("----check百度tatus中")
checkQRLoginStatus(sign)
} catch (e: UnsupportedEncodingException) {
throw RuntimeException(e)
}
}, 3, 3, TimeUnit.SECONDS)
}
}

View File

@ -1,797 +0,0 @@
package com.github.catvod.api
import com.github.catvod.bean.Result
import com.github.catvod.bean.Vod
import com.github.catvod.bean.Vod.VodPlayBuilder
import com.github.catvod.net.OkHttp
import com.github.catvod.utils.Json
import com.github.catvod.utils.ProxyServer.buildProxyUrl
import com.github.catvod.utils.Util
import com.github.catvod.utils.Util.MEDIA
import com.google.gson.JsonObject
import java.net.URLEncoder
import java.util.*
object BaiduDrive {
private val cache = mutableMapOf<String, String>();
private val headers = mapOf(
"User-Agent" to "Mozilla/5.0 (Linux; Android 12; SM-X800) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.40 Safari/537.36",
"Accept" to "application/json, text/plain, */*",
"Content-Type" to "application/x-www-form-urlencoded",
"Origin" to "https://pan.baidu.com",
"Referer" to "https://pan.baidu.com/"
)
//是否删除过文件标志
private var deleteTag = 0;
private val saveDirName = "TVBOX_BD"
private var cookies = BaiDuYunHandler.get().token
private val apiHost = "https://pan.baidu.com"
private val displayName = listOf("BD原画")
fun setCookie(extend: String) {
if (extend.isEmpty()) return
cookies = extend
}
fun processShareLinks(urls: List<String>): Pair<List<String>, List<String>> {
//首先确保cookie不为空
if (cookies.isEmpty()) {
BaiDuYunHandler.get().startScan()
cookies = BaiDuYunHandler.get().token
}
if (urls.isEmpty()) return emptyList<String>() to emptyList()
val results = urls.map { url ->
processSingleLink(url)
}
val names = mutableListOf<String>()
val allVideos = mutableListOf<String>()
results.forEach { result ->
if (result != null) {
val (avideos, videos) = result
names.addAll(displayName)
allVideos.add(avideos.joinToString("#"))
//allVideos.add(videos.joinToString("#"))
}
}
return names to allVideos
}
private fun processSingleLink(url: String): Pair<List<String>, List<String>>? {
return try {
val urlInfo = parseShareUrl(url)
if (urlInfo.containsKey("error")) return null
val tokenInfo = getShareToken(urlInfo)
if (tokenInfo?.containsKey("error") == true) return null
getAllVideos(tokenInfo!!)
} catch (e: Exception) {
e.printStackTrace()
null
}
}
private fun parseShareUrl(url: String): Map<String, String> {
var lurl = url
if ("提取码" in url) lurl = url.replace("提取码:", "?pwd=")
if ("/share/" !in url) {
val response = OkHttp.getLocation(url, headers)
lurl = response ?: ""
}
val queryParams = parseQueryParams(lurl)
val finalUrl = if ("/share/" in url) url.replace("share/init?surl=", "s/1") else url
return mapOf<String, String>(
"url" to finalUrl,
"surl" to (queryParams["surl"]?.firstOrNull() ?: ""),
"pwd" to (queryParams["pwd"]?.firstOrNull() ?: "")
)
}
private fun getShareToken(urlInfo: Map<String, String>): Map<String, String>? {
val params = mapOf(
"t" to System.currentTimeMillis().toString(), "surl" to (urlInfo["surl"] ?: "")
)
val data = mapOf("pwd" to (urlInfo["pwd"] ?: ""))
val response = OkHttp.post(
"$apiHost/share/verify?t=${params["t"]}&surl=${params["surl"]}", data, headers
)
// if ("error" in response) return response
val json = Json.safeObject(response.body)
val randsk = json.asJsonObject.get("randsk").asString ?: return mapOf("error" to "获取randsk失败")
return mapOf(
"yurl" to (urlInfo["url"]?.split("s/")?.last()?.split("?")?.first() ?: ""),
"randsk" to randsk,
"surl" to (urlInfo["surl"] ?: "")
)
}
private fun getAllVideos(tokenInfo: Map<String, String>): Pair<List<String>, List<String>> {
val videos = mutableListOf<String>()
val avideos = mutableListOf<String>()
val seenFolders = mutableSetOf<String>()
val pendingFolders = mutableListOf<Map<String, Any>>()
try {
// 处理根目录
var currentPage = 1
var uk = ""
var shareid = ""
while (true) {
val rootFolder = mutableMapOf(
"surl" to tokenInfo["surl"]!!,
"randsk" to tokenInfo["randsk"]!!,
"page" to currentPage,
"is_root" to true
)
val rootResult = getFolderContents(rootFolder)
// if ("error" in rootResult) break
val data = rootResult?.asJsonObject
val items = data?.get("list")?.asJsonArray ?: break
if (items.isEmpty) break
// 第一页获取uk和shareid
if (currentPage == 1) {
uk = data["uk"]?.toString() ?: ""
shareid = data["share_id"]?.toString() ?: ""
if (uk.isEmpty() || shareid.isEmpty()) return emptyList<String>() to emptyList()
}
// 处理items
items.forEach { item ->
if (item.asJsonObject["isdir"].asInt == 1) {
val folderPath =
"/sharelink$uk-${item.asJsonObject["fs_id"].asString}/${item.asJsonObject["server_filename"].asString}"
if (folderPath !in seenFolders) {
seenFolders.add(folderPath)
pendingFolders.add(
mapOf(
"surl" to tokenInfo["surl"]!!,
"randsk" to tokenInfo["randsk"]!!,
"uk" to uk,
"shareid" to shareid,
"dir" to folderPath,
"page" to 1
)
)
}
} else if (isVideoFile(item.asJsonObject["server_filename"].asString ?: "")) {
addVideo(item.asJsonObject, uk, shareid, tokenInfo, avideos, videos)
}
}
if (items.size() < 9999) break
currentPage++
}
// 处理子文件夹
while (pendingFolders.isNotEmpty()) {
val currentBatch = pendingFolders.toList()
pendingFolders.clear()
val results = currentBatch.map { folderInfo ->
getFolderContents(folderInfo)
}
results.forEachIndexed { i, result ->
val folderInfo = currentBatch[i]
//if ("error" in result) return@forEachIndexed
val items = result?.asJsonObject?.get("list")?.asJsonArray ?: return@forEachIndexed
items.forEach { item ->
if (item.asJsonObject["isdir"].asInt == 1) {
val folderPath = item.asJsonObject["path"].asString ?: ""
if (folderPath !in seenFolders) {
seenFolders.add(folderPath)
pendingFolders.add(
mapOf(
"surl" to tokenInfo["surl"]!!,
"randsk" to tokenInfo["randsk"]!!,
"uk" to folderInfo["uk"]!!,
"shareid" to folderInfo["shareid"]!!,
"dir" to folderPath,
"page" to 1
)
)
}
} else if (isVideoFile(item.asJsonObject["server_filename"].asString ?: "")) {
addVideo(
item.asJsonObject,
folderInfo["uk"]?.toString() ?: "",
folderInfo["shareid"]?.toString() ?: "",
tokenInfo,
avideos,
videos
)
}
}
if (items.size() >= 9999) {
pendingFolders.add(folderInfo.toMutableMap().apply {
this["page"] = (this["page"] as Int) + 1
})
}
}
}
return avideos to videos
} catch (e: Exception) {
e.printStackTrace()
return emptyList<String>() to emptyList()
}
}
private fun isVideoFile(string: String): Boolean {
return string.substringAfterLast(".").lowercase(Locale.ROOT) in MEDIA
}
private fun addVideo(
item: JsonObject,
uk: String,
shareid: String,
tokenInfo: Map<String, String>,
avideos: MutableList<String>,
videos: MutableList<String>
) {
val sizeStr = formatSize(item["size"].asLong)
val name = item["server_filename"] ?: ""
val originalData = mapOf(
"uk" to uk,
"shareid" to shareid,
"fid" to item["fs_id"],
"randsk" to tokenInfo["randsk"],
"pname" to name,
"qtype" to "original"
)
val previewData = mapOf(
"uk" to uk,
"fid" to item["fs_id"],
"shareid" to shareid,
"surl" to tokenInfo["yurl"],
"pname" to name,
"qtype" to "preview"
)
avideos.add(
"[$sizeStr]$name$${
Util.base64Encode(Json.toJson(originalData).toByteArray())
}"
)
videos.add(
"[$sizeStr]$name$${
Util.base64Encode(Json.toJson(previewData).toByteArray())
}"
)
}
private fun getFolderContents(folderInfo: Map<String, Any>): JsonObject? {
val params = if (folderInfo.containsKey("dir")) {
mapOf(
"uk" to folderInfo["uk"]!!.toString(),
"shareid" to folderInfo["shareid"]!!.toString(),
"page" to folderInfo["page"].toString(),
"num" to "9999",
"dir" to URLEncoder.encode(folderInfo["dir"]!!.toString()),
"desc" to "0",
"order" to "name",
)
} else {
mapOf(
"page" to folderInfo["page"].toString(),
"num" to "9999",
"shorturl" to folderInfo["surl"]!!.toString(),
"root" to "1",
"desc" to "0",
"order" to "name",
)
}
val tempHeader = headers.toMutableMap()
tempHeader.put("Cookie", "BDCLND=${folderInfo["randsk"]}")
val result = OkHttp.string("$apiHost/share/list", params, tempHeader)
return Json.safeObject(result)
}
private fun parseQueryParams(url: String): Map<String, List<String>> {
val query = url.substringAfter(
"?"
).substringBefore('#')
return query.split('&').associate {
val (key, value) = it.split('=', limit = 2)
key to listOf(value)
}
}
private fun formatSize(bytes: Long): String {
if (bytes <= 0) return "0 B"
val units = arrayOf("B", "KB", "MB", "GB", "TB")
val digitGroups = (Math.log10(bytes.toDouble()) / Math.log10(1024.0)).toInt()
return "%.1f %s".format(bytes / Math.pow(1024.0, digitGroups.toDouble()), units[digitGroups])
}
fun getBdUid(): String? {
if (cache["uid"] != null) {
return cache["uid"]
}
val tempHeader = headers.toMutableMap()
tempHeader.put("Cookie", cookies)
try {
val response = OkHttp.string(
"https://mbd.baidu.com/userx/v1/info/get?appname=baiduboxapp&fields=%20%20%20%20%20%20%20%20%5B%22bg_image%22,%22member%22,%22uid%22,%22avatar%22,%20%22avatar_member%22%5D&client&clientfrom&lang=zh-cn&tpl&ttt",
emptyMap<String, String>(),
tempHeader
)
val responseJson = Json.safeObject(response)
val user = responseJson["data"].asJsonObject
val fields = user?.get("fields")?.asJsonObject
val uidValue = fields?.get("uid")?.asString
if (uidValue != null) {
cache["uid"] = uidValue
return uidValue
} else {
throw Exception("Failed to retrieve UID from Baidu Drive.")
}
} catch (e: Exception) {
println("获取百度网盘用户ID失败: ${e.message}")
return ""
}
}
fun _getSign(videoData: JsonObject): Pair<String, String> {
val tempHeader = headers.toMutableMap()
tempHeader.put("Cookie", cookies)
val response: String? = OkHttp.string(
"${apiHost}/share/tplconfig", mapOf<String, String>(
"surl" to videoData["surl"].asString, "fields" to "cfrom_id,Espace_info,card_info,sign,timestamp"
),
tempHeader
)
return try {
val data = Json.safeObject(response)["data"].asJsonObject
data["sign"].asString to data["timestamp"].asString
} catch (_: Exception) {
"" to ""
}
}
fun _getDownloadUrl(videoData: JsonObject): String {
return try {
var cookie = ""
val BDCLND = "BDCLND=" + videoData["randsk"].asString
// 更新Cookie中的BDCLND值
if (!this.cookies.contains("BDCLND")) {
cookie = this.cookies + ";" + BDCLND
} else {
cookie = this.cookies.split(";").joinToString(";") {
if (it.contains("BDCLND")) {
BDCLND
} else {
it
}
}
}
val transferHeaders = mapOf(
"User-Agent" to "Android",
"Connection" to "Keep-Alive",
"Content-Type" to "application/x-www-form-urlencoded",
"Accept-Language" to "zh-CN,zh;q=0.8",
"charset" to "UTF-8",
"Referer" to "https://pan.baidu.com",
"Cookie" to cookie
)
// 先清空文件夹在创建文件夹
if (deleteTag == 0) {
_deleteTransferFile("/$saveDirName")
//创建路径
createSaveDir()
deleteTag = 1
}
val data =
"from=${videoData["uk"].asString}&shareid=${videoData["shareid"].asString}&ondup=newcopy&path=/${saveDirName}&fsidlist=[${videoData["fid"].asString}]"
var to = ""
for (i in 1..30) {
val response =
OkHttp.post("${apiHost}/share/transfer?${data}", emptyMap<String, String>(), transferHeaders)
val result = Json.safeObject(response.body)
try {
to = (result["extra"].asJsonObject)["list"].asJsonArray[0].asJsonObject["to"].asString
// videoData["to"] = to
if (to.isNotEmpty()) {
println("成功转存文件到: $to")
break
}
} catch (e: Exception) {
println("解析转存响应出错: ${e.message}")
continue
}
}
if (to.isEmpty()) {
println("转存文件失败,无法获取下载链接")
return ""
}
val mediaInfoHeaders = mapOf(
"User-Agent" to "netdisk;1.4.2;22021211RC;android-android;12;JSbridge4.4.0;jointBridge;1.1.0;",
"Connection" to "Keep-Alive",
"Accept-Language" to "zh-CN,zh;q=0.8",
"charset" to "UTF-8",
"Cookie" to cookie
).toMutableMap()
val mediaInfoParams = mapOf(
"type" to "M3U8_FLV_264_480", "path" to "/$to", "clienttype" to "80", "origin" to "dlna"
)
val mediaInfoResponse: String? = OkHttp.string(
"${apiHost}/api/mediainfo", mediaInfoParams, mediaInfoHeaders
)
val responseJson = Json.safeObject(mediaInfoResponse)
val info = responseJson["info"].asJsonObject
val downloadUrl = info["dlink"].asString
println("获取到下载链接: $downloadUrl")
downloadUrl
} catch (e: Exception) {
println("获取下载链接过程中出错: ${e.message}")
e.printStackTrace()
""
}
}
fun getVideoUrl(videoData: JsonObject, flag: String): Map<String, Any> {
return try {
val bdUid = getBdUid()
println("获取百度网盘用户ID: $bdUid")
if (flag.contains("原画")) {
var headersApp = mapOf("User-Agent" to "netdisk;P2SP;2.2.91.136;android-android;")
var downloadUrl = _getAppDownloadUrl(videoData)
if (downloadUrl.isEmpty()) {
headersApp = mapOf(
"User-Agent" to "netdisk;1.4.2;22021211RC;android-android;12;JSbridge4.4.0;jointBridge;1.1.0;"
)
downloadUrl = _getDownloadUrl(videoData)
}
if (downloadUrl.isNotEmpty()) {
val result = mapOf(
"parse" to "0", "url" to downloadUrl, "header" to headersApp
)
result
} else {
_handleError
}
} else {
val (sign, time) = _getSign(videoData)
if (sign.isEmpty() || time.isEmpty()) {
return _handleError
}
val plist = _getPlayList(videoData, sign, time)
val tempHeader = headers.toMutableMap()
tempHeader.put("Cookie", cookies)
mapOf(
"parse" to "0", "url" to plist[0], "header" to tempHeader
)
}
} catch (e: Exception) {
println("获取播放链接失败: ${e.message}")
_handleError
}
}
private fun _getAppDownloadUrl(videoData: JsonObject): String {
return try {
val headers = mapOf<String, String>(
"User-Agent" to "netdisk;P2SP;2.2.91.136;android-android;",
"Connection" to "Keep-Alive",
"Accept-Language" to "zh-CN,zh;q=0.8",
"charset" to "UTF-8",
"cookie" to cookies
)
val uid = this.getBdUid()
val t = System.currentTimeMillis()
val params = mapOf<String, String>(
"shareid" to videoData["shareid"].asString,
"uk" to videoData["uk"].asString,
"fid" to videoData["fid"].asString,
"sekey" to unquote(videoData["randsk"].asString),
"origin" to "dlna",
"devuid" to "73CED981D0F186D12BC18CAE1684FFD5|VSRCQTF6W",
"clienttype" to "1",
"channel" to "android_12_zhao_bd-netdisk_1024266h",
"version" to "11.30.2",
"time" to t.toString()
).toMutableMap()
val randstr = this.sha1(
this.sha1(
Util.findByRegex(
"BDUSS=(.+?);", cookies, 1
)
) + uid + "ebrcUYiuxaZv2XGu7KIYKxUrqfnOfpDF$t${params["devuid"]}11.30.2ae5821440fab5e1a61a025f014bd8972"
)
params.put("rand", randstr)
val response = OkHttp.string(
"${apiHost}/share/list", params, headers
)
val json = Json.safeObject(response)
val dlink = json["list"].asJsonArray[0].asJsonObject["dlink"].asString
/* val url = response["data"] as Map<String, Any>
val list = url["list"] as List<Map<String, Any>>
val dlink = list[0]["dlink"] as String
val pDataResponse = client.get(dlink) {
headers { this@_getAppDownloadUrl.headers.forEach { name, value -> append(name, value) } }
cookies?.let { setCookie(it) }
followRedirects = false
timeout.socketTimeoutMillis = 10000
}
val pUrl = pDataResponse.headers[HttpHeaders.Location]?.toString()
pUrl ?: dlink*/
dlink
} catch (e: Exception) {
println("获取下载链接失败: ${e.message}")
""
}
}
private fun _getPlayList(videoData: JsonObject, sign: String, time: String): List<String> {
val hz = getPlayFormatList()
val plist = mutableListOf<String>()
for (quality in hz) {
val url =
("${apiHost}/share/streaming?" + "uk=${videoData["uk"].asString}&" + "fid=${videoData["fid"].asString}&" + "sign=$sign&" + "timestamp=$time&" + "shareid=${videoData["shareid"].asString}&" + "type=M3U8_AUTO_${
quality.replace(
"P", ""
)
}")
plist.add(url)
}
return plist
}
/**
* 创建保存目录
*/
fun createSaveDir(): Long? {
var saveDirId: Long? = null
// 创建保存目录
if (cookies.isEmpty()) {
return null
}
val tempHeader = headers.toMutableMap()
tempHeader.put("Cookie", cookies)
return try {
val listResp = OkHttp.string(
"${apiHost}/api/list", mapOf(
"dir" to "/",
"order" to "name",
"desc" to "0",
"showempty" to "0",
"web" to "1",
"app_id" to "250528"
), tempHeader
)
val json = Json.safeObject(listResp)
if (json["errno"].asInt != 0) {
return null
}
val drpyDir = json["list"].asJsonArray.find { item ->
item.asJsonObject.get("isdir").asInt == 1 && item.asJsonObject.get("server_filename").asString == saveDirName
}
if (drpyDir != null) {
saveDirId = drpyDir.asJsonObject.get("fs_id").asLong
return saveDirId
}
val createResp = OkHttp.post(
"${apiHost}/api/create?a=commit&bdstoken=${getBdstoken()}&clienttype=0&app_id=250528&web=1&dp-logid=73131200762376420075",
mapOf(
"path" to "//$saveDirName",
"isdir" to "1",
"block_list" to "[]",
),
tempHeader
)
val createJson = Json.safeObject(createResp.body)
saveDirId = createJson["fs_id"].asLong
saveDirId
} catch (e: Exception) {
println("创建保存目录失败: ${e.message}")
null
}
}
fun getBdstoken(): String {
if (cache["bdstoken"] != null) {
return cache["bdstoken"]!!
}
val tempHeader = headers.toMutableMap()
tempHeader.put("Cookie", cookies)
val userInfo = OkHttp.string(
"${apiHost}/api/gettemplatevariable?clienttype=0&app_id=250528&web=1&fields=[\"bdstoken\",\"token\",\"uk\",\"isdocuser\",\"servertime\"]",
mapOf(),
tempHeader
)
val json = Json.safeObject(userInfo)
val bdstoken: String? = json["result"]?.asJsonObject?.get("bdstoken")?.asString
cache["bdstoken"] = bdstoken ?: ""
return bdstoken ?: ""
}
private fun _deleteTransferFile(filePath: String) {
try {
val url =
"$apiHost/api/filemanager?async=2&onnest=fail&opera=delete&bdstoken=${getBdstoken()}&newVerify=1&clienttype=0&app_id=250528&web=1&dp-logid=39292100213290200076"
val params = mapOf(
"filelist" to "[\"$filePath\"]"
)
val headers = mutableMapOf(
"User-Agent" to "Android",
"Connection" to "Keep-Alive",
"Accept-Encoding" to "br,gzip",
"Content-Type" to "application/x-www-form-urlencoded; charset=utf-8",
"Accept-Language" to "zh-CN,zh;q=0.8",
"charset" to "UTF-8",
"Cookie" to cookies,
)
val response = OkHttp.post(url, params, headers)
println("删除文件响应: ${response.body}")
println("响应状态码: ${response.code}")
} catch (e: Exception) {
println("删除文件出错: ${e.message}")
e.printStackTrace()
}
}
private fun unquote(encoded: String): String {
return encoded.replace("%([0-9A-Fa-f]{2})".toRegex()) { match ->
Integer.parseInt(match.groupValues[1], 16).toChar().toString()
}
}
private fun sha1(input: String): String {
return Util.sha1Hex(input)
}
private val _handleError = mapOf(
"parse" to "1", "msg" to "Error retrieving video URL"
)
fun getVod(shareUrl: String): Vod {
val (froms, urls) = processShareLinks(listOf(shareUrl))
val builder = VodPlayBuilder()
for (i in froms.indices) {
val playUrls = mutableListOf<VodPlayBuilder.PlayUrl>();
for (url in urls[i].split("#")) {
val arr = url.split("$")
val play = VodPlayBuilder.PlayUrl()
play.name = arr[0]
play.url = arr[1]
playUrls.add(play)
}
builder.append(froms[i], playUrls)
}
val buildResult = builder.build();
val vod = Vod()
vod.setVodId(shareUrl)
vod.setVodPic("")
vod.setVodYear("")
vod.setVodName("")
vod.setVodContent("")
vod.setVodPlayFrom(buildResult.vodPlayFrom)
vod.setVodPlayUrl(buildResult.vodPlayUrl)
return vod
}
fun playerContent(json: JsonObject, flag: String): String {
val play = getVideoUrl(json, flag);
val header = play["header"] as Map<String, String>
return Result.get().url(buildProxyUrl(play["url"] as String, header)).octet().header(header).string();
}
fun getPlayFormatList(): Array<String> {
return listOf("1080P").toTypedArray()
}
fun proxyVideo(params: MutableMap<String, String>): Array<Any> {
return emptyList<Any>().toTypedArray()
}
}

View File

@ -1,398 +0,0 @@
package com.github.catvod.api
import com.github.catvod.bean.Result
import com.github.catvod.bean.Vod
import com.github.catvod.bean.Vod.VodPlayBuilder
import com.github.catvod.bean.pan123.ShareInfo
import com.github.catvod.crawler.SpiderDebug
import com.github.catvod.net.OkHttp
import com.github.catvod.utils.Json
import com.github.catvod.utils.ProxyServer.buildProxyUrl
import com.github.catvod.utils.Util
import com.google.gson.JsonObject
import okhttp3.HttpUrl
import java.util.regex.Pattern
/**
* 123网盘API操作类
* 提供123网盘的文件分享下载播放等功能
*/
object Pan123Api {
public const val regex =
"https://(123592\\.com|123912\\.com|123865\\.com|123684\\.com|www\\.123684\\.com|www\\.123865\\.com|www\\.123912\\.com|www\\.123pan\\.com|www\\.123pan\\.cn|www\\.123592\\.com)/s/([^/]+)"
private const val api = "https://www.123684.com/b/api/share/"
private const val loginUrl = "https://login.123pan.com/api/user/sign_in"
private var authToken = ""
/**
* 初始化方法检查登录状态
*/
fun init() {
}
/**
* 获取认证token
*/
private fun getAuth(): String {
if (authToken.isNotBlank()) {
return authToken
}
return Pan123Handler.getAuth()
}
/**
* 登录方法
*/
fun login(passport: String, password: String): JsonObject? {
val data = mapOf(
"passport" to passport, "password" to password, "remember" to true
)
val headers = mapOf(
"User-Agent" to "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36",
"Content-Type" to "application/json",
"App-Version" to "43",
"Referer" to "https://login.123pan.com/centerlogin?redirect_url=https%3A%2F%2Fwww.123684.com&source_page=website",
"Accept" to "application/json, text/plain, */*",
"Origin" to "https://login.123pan.com",
"Connection" to "keep-alive",
"Accept-Language" to "zh-CN,zh;q=0.9"
)
try {
val response = OkHttp.post(loginUrl, Json.toJson(data), headers)
SpiderDebug.log("登录请求信息:")
SpiderDebug.log("URL: $loginUrl")
SpiderDebug.log("账号: ${passport}")
SpiderDebug.log("响应内容: ${response.body}")
if (response.code == 200) {
val authData = Json.safeObject(response.body)
SpiderDebug.log("解析后的响应数据: $authData")
if (authData.has("data") && authData.getAsJsonObject("data").has("token")) {
val token = authData.getAsJsonObject("data").get("token").asString
// setAuth(token)
SpiderDebug.log("登录成功")
authToken=token
return authData.get("data").asJsonObject
}
}
throw Exception("登录失败: HTTP状态码=${response.code}, 响应=${response.body}")
} catch (e: Exception) {
SpiderDebug.log("登录过程中发生错误: ${e.message}")
throw e
}
}
/**
* 解析分享链接提取分享密钥和提取码
*/
fun getShareData(url: String): Map<String, String> {
SpiderDebug.log("123链接$url")
var sharePwd = ""
var lurl = java.net.URLDecoder.decode(url, "UTF-8")
// 处理提取码格式
if ("提取码" in lurl && "?" !in lurl) {
lurl = lurl.replace(Regex("提取码[:]"), "?")
}
if ("提取码" in lurl && "?" in lurl) {
lurl = lurl.replace(Regex("提取码[:]?"), "")
}
if ("" in lurl) {
lurl = lurl.replace("", "")
}
val matcher = Pattern.compile(regex).matcher(lurl)
// 提取分享密码
if ("?" in lurl) {
val queryPart = lurl.split("?")[1]
val pwdMatcher = Regex("[A-Za-z0-9]+").find(queryPart)
if (pwdMatcher != null) {
sharePwd = queryPart.split("=")[1]
}
}
if (matcher.find()) {
val match = matcher.group(2) ?: ""
val key = when {
"?" in match -> match.split("?")[0]
".html" in match -> match.replace(".html", "")
"www" in match -> matcher.group(1) ?: match
else -> match
}
return mapOf("key" to key, "sharePwd" to sharePwd)
}
return emptyMap()
}
/**
* 根据分享链接获取文件列表
*/
fun getFilesByShareUrl(shareKey: String, sharePwd: String): List<ShareInfo> {
// 获取分享信息
val cate = getShareInfo(shareKey, sharePwd, 0, 0)
return cate
}
/**
* 获取分享信息
*/
private fun getShareInfo(
shareKey: String, sharePwd: String, next: Int, parentFileId: Long
): List<ShareInfo> {
//文件夹
val folders = mutableListOf<ShareInfo>()
//视频
val videos = mutableListOf<ShareInfo>()
val url =
"${api}get?limit=100&next=$next&orderBy=file_name&orderDirection=asc&shareKey=${shareKey.trim()}&SharePwd=${sharePwd.ifEmpty { "" }}&ParentFileId=$parentFileId&Page=1"
try {
val response = OkHttp.string(
url, mapOf(
"User-Agent" to "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36"
)
)
val data = Json.safeObject(response)
if (data.has("code") && data.get("code").asInt == 5103) {
SpiderDebug.log("123获取文件列表出错" + data.get("message").asString)
return emptyList()
}
val info = data.getAsJsonObject("data")
//其下没有文件
if (info.get("Len").asLong <= 0) {
return emptyList()
}
val nextValue = info.get("Next").asInt
val infoList = info.getAsJsonArray("InfoList")
// 处理文件夹
for (item in infoList) {
val itemObj = item.asJsonObject
//文件夹
if (itemObj.get("Category").asInt == 0) {
folders.add(
ShareInfo(
itemObj.get("FileName").asString,
shareKey,
sharePwd,
nextValue,
itemObj.get("FileId").asLong,
itemObj["S3KeyFlag"].asString,
itemObj["Size"].asLong,
itemObj["Etag"].asString,
)
)
} else if (itemObj.get("Category").asInt == 2) {
videos.add(
ShareInfo(
itemObj.get("FileName").asString,
shareKey,
sharePwd,
nextValue,
itemObj.get("FileId").asLong,
itemObj["S3KeyFlag"].asString,
itemObj["Size"].asLong,
itemObj["Etag"].asString,
)
)
}
}
// 递归获取子文件夹信息
for (item in folders) {
val result = getShareInfo(
item.shareKey, item.sharePwd, item.next, item.fileId
)
videos.addAll(result)
}
return videos
} catch (e: Exception) {
SpiderDebug.log("获取分享信息时发生错误: ${e.message}")
return emptyList()
}
}
/**
* 获取文件下载链接
*/
fun getDownload(
shareKey: String, fileId: Long, s3KeyFlag: String, size: Long, etag: String
): String {
SpiderDebug.log("获取下载链接参数:")
SpiderDebug.log("ShareKey: $shareKey")
SpiderDebug.log("FileID: $fileId")
SpiderDebug.log("S3KeyFlag: $s3KeyFlag")
SpiderDebug.log("Size: $size")
SpiderDebug.log("Etag: $etag")
SpiderDebug.log("Auth Token: ${getAuth().take(30)}...")
val data = mapOf(
"ShareKey" to shareKey, "FileID" to fileId, "S3KeyFlag" to s3KeyFlag, "Size" to size, "Etag" to etag
)
val url = "${api}download/info"
SpiderDebug.log("请求URL: $url")
SpiderDebug.log("请求数据: ${Json.toJson(data)}")
val headers = mapOf(
"User-Agent" to "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36",
"Content-Type" to "application/json",
"Authorization" to "Bearer ${getAuth()}",
"platform" to "android"
)
try {
val response = OkHttp.post(url, Json.toJson(data), headers)
SpiderDebug.log("响应状态码: ${response.code}")
SpiderDebug.log("响应内容: ${response.body}")
if (response.code == 200) {
val responseData = Json.safeObject(response.body)
if (responseData.has("data") && responseData.getAsJsonObject("data").has("DownloadURL")) {
val encodeUrl = responseData.getAsJsonObject("data").get("DownloadURL").asString
return Util.base64Decode(HttpUrl.parse(encodeUrl)?.queryParameter("params"))
}
}
throw Exception("获取下载链接失败: HTTP状态码=${response.code}, 响应=${response.body}")
} catch (e: Exception) {
SpiderDebug.log("获取下载链接时发生错误: ${e.message}")
throw e
}
}
/**
* 获取视频在线播放链接
*/
fun getLiveTranscoding(
shareKey: String, fileId: Long, s3KeyFlag: String, size: Long, etag: String
): List<Map<String, String>> {
val url = "https://www.123684.com/b/api/video/play/info?" + "etag=$etag&size=$size&from=1&shareKey=$shareKey"
val headers = mapOf(
"User-Agent" to "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36",
"Authorization" to "Bearer ${getAuth()}",
"Content-Type" to "application/json;charset=UTF-8",
"platform" to "android"
)
try {
val response = OkHttp.string(url, headers)
val downData = Json.safeObject(response)
if (downData.has("data") && downData.getAsJsonObject("data").has("video_play_info")) {
val videoInfo = mutableListOf<Map<String, String>>()
val videoPlayInfo = downData.getAsJsonObject("data").getAsJsonArray("video_play_info")
for (item in videoPlayInfo) {
val itemObj = item.asJsonObject
if (itemObj.has("url") && !itemObj.get("url").isJsonNull) {
val resolution = if (itemObj.has("resolution")) itemObj.get("resolution").asString else ""
val urlValue = itemObj.get("url").asString
videoInfo.add(
mapOf(
"name" to resolution, "url" to urlValue
)
)
}
}
return videoInfo
}
} catch (e: Exception) {
SpiderDebug.log("获取视频播放链接时发生错误: ${e.message}")
}
return emptyList()
}
fun getPlayFormatList(): Array<String> {
return arrayOf("原画")
}
fun getVod(key: String, sharePwd: String): Vod {
val list = getFilesByShareUrl(key, sharePwd)
val builder = VodPlayBuilder()
for (i in getPlayFormatList().indices) {
val playUrls = mutableListOf<VodPlayBuilder.PlayUrl>();
for (item in list) {
val play = VodPlayBuilder.PlayUrl()
play.name = "[${Util.getSize(item.Size.toDouble())}]${item.filename}"
play.url =
listOf(item.shareKey, item.fileId, item.S3KeyFlag, item.Size, item.Etag).joinToString("\\+\\+")
playUrls.add(play)
}
builder.append("pan123" + getPlayFormatList()[i], playUrls)
}
val buildResult = builder.build();
val vod = Vod()
vod.setVodId(key + "++" + sharePwd)
vod.setVodPic("")
vod.setVodYear("")
vod.setVodName("")
vod.setVodContent("")
vod.setVodPlayFrom(buildResult.vodPlayFrom)
vod.setVodPlayUrl(buildResult.vodPlayUrl)
return vod
}
fun playerContent(id: String, flag: String): String {
val itemJson = id.split("\\+\\+")
SpiderDebug.log("播放参数:$itemJson")
val url = getDownload(
itemJson[0], itemJson[1].toLong(), itemJson[2], itemJson[3].toLong(), itemJson[4]
)
val header = mapOf(
"User-Agent" to "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36"
)
return Result.get().url(buildProxyUrl(url, header)).octet().header(header).string();
}
}

View File

@ -1,142 +0,0 @@
package com.github.catvod.api
import android.R
import android.app.AlertDialog
import android.content.DialogInterface
import android.view.ViewGroup
import android.widget.EditText
import android.widget.LinearLayout
import com.github.catvod.api.Pan123Api.login
import com.github.catvod.bean.pan123.Cache
import com.github.catvod.bean.pan123.User
import com.github.catvod.crawler.SpiderDebug
import com.github.catvod.spider.Init
import com.github.catvod.utils.Notify
import com.github.catvod.utils.Path
import com.github.catvod.utils.ResUtil
import org.apache.commons.lang3.StringUtils
import java.io.File
object Pan123Handler {
private var cache: Cache? = null
private var dialog: AlertDialog? = null
private var auth = ""
private var userName = ""
private var passwd = ""
private var expire = 0L;
fun getCache(): File {
return Path.tv("pan123")
}
fun getAuth(): String {
return auth
}
init {
cache = Cache.objectFrom(Path.read(getCache()))
if (cache == null) {
SpiderDebug.log("cache 为null")
startFlow()
} else {
userName = cache!!.user.userName
passwd = cache!!.user.password
auth = cache!!.user.cookie
expire = cache!!.user.expire
if (StringUtils.isNotBlank(userName) && StringUtils.isNotBlank(passwd)) {
if (StringUtils.isBlank(auth) || expire == 0L || System.currentTimeMillis() > expire) {
SpiderDebug.log("auth 为空或者登录已过期")
this.loginWithPassword(userName, passwd)
}
} else {
SpiderDebug.log("userName passwd 为空")
startFlow()
}
}
}
fun loginWithPassword(uname: String?, passwd: String?) {
SpiderDebug.log("loginWithPassword uname: $unamepasswd$passwd")
try {
//保存的账号密码
val json = login(uname!!, passwd!!)
if (json != null) {
val user = User()
user.cookie = json.get("token").asString
user.password = passwd
user.userName = uname
user.expire = json.get("refresh_token_expire_time").asLong * 1000
this.auth = json.get("token").asString
cache?.setUserInfo(user)
Notify.show("123登录成功")
} else {
Notify.show("123登录失败")
}
} catch (e: Exception) {
SpiderDebug.log("登录失败: " + e.message)
Notify.show("123登录失败: " + e.message)
}
}
fun startFlow() {
Init.run { this.showInput() }
}
private fun showInput() {
try {
val margin = ResUtil.dp2px(16)
val params =
LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
val frame = LinearLayout(Init.context())
frame.setOrientation(LinearLayout.VERTICAL)
// frame.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
// params.setMargins(margin, margin, margin, margin);
val username = EditText(Init.context())
username.setHint("请输入123用户名")
val password = EditText(Init.context())
password.setHint("请输入123密码")
frame.addView(username, params)
frame.addView(password, params)
dialog = AlertDialog.Builder(Init.getActivity()).setTitle("请输入123用户名和密码").setView(frame)
.setNegativeButton(
R.string.cancel, null
).setPositiveButton(
R.string.ok, DialogInterface.OnClickListener { dialog: DialogInterface?, which: Int ->
onPositive(
username.getText().toString(), password.getText().toString()
)
}).show()
} catch (ignored: Exception) {
}
}
private fun onPositive(username: String?, password: String?) {
SpiderDebug.log("123用户名: $username")
SpiderDebug.log("123密码: $password")
dismiss()
Init.execute(Runnable {
loginWithPassword(username, password)
})
}
private fun dismiss() {
try {
if (dialog != null) dialog!!.dismiss()
} catch (ignored: Exception) {
}
}
}

View File

@ -12,6 +12,7 @@ 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;
@ -25,6 +26,7 @@ import com.github.catvod.spider.Init;
import com.github.catvod.spider.Proxy;
import com.github.catvod.utils.*;
import com.google.gson.Gson;
import org.apache.commons.lang3.StringUtils;
import java.io.ByteArrayInputStream;
@ -71,7 +73,7 @@ public class QuarkApi {
if (Util.getExt(url).contains("m3u8")) {
return getM3u8(url, header);
}
return ProxyVideo.proxyMultiThread(url, header);
return ProxyVideo.proxy(url, header);
}
/**
@ -210,20 +212,17 @@ 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");
if (flag.contains("quark原画")) {
playUrl = this.getDownload(shareId, stoken, fileId, fileToken, true);
return Result.get().url(ProxyServer.INSTANCE.buildProxyUrl(playUrl, header)).octet().header(header).string();
} else {
playUrl = this.getLiveTranscoding(shareId, stoken, fileId, fileToken, flag);
return Result.get().url(proxyVideoUrl(playUrl, header)).octet().header(header).string();
}
}
private String proxyVideoUrl(String url, Map<String, String> header) {
return String.format(Proxy.getUrl() + "?do=quark&type=video&url=%s&header=%s", Util.base64Encode(url.getBytes(Charset.defaultCharset())), Util.base64Encode(Json.toJson(header).getBytes(Charset.defaultCharset())));
}
@ -378,7 +377,7 @@ public class QuarkApi {
public List<String> getPlayFormatList() {
if (this.isVip) {
return Arrays.asList("4K"/*, "超清", "高清", "普画"*/);
return Arrays.asList("4K", "超清", "高清", "普画");
} else {
return Collections.singletonList("普画");
}
@ -414,7 +413,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")) && (Util.isMedia((String) item.get("file_name")))) {
} else if (Boolean.TRUE.equals(item.get("file")) && "video".equals(item.get("obj_category"))) {
if ((Double) item.get("size") < 1024 * 1024 * 5) continue;
item.put("stoken", this.shareTokenCache.get(shareData.getShareId()).get("stoken"));
videos.add(Item.objectFrom(item, shareData.getShareId(), shareIndex));
@ -475,17 +474,14 @@ public class QuarkApi {
}
private void clearSaveDir() throws Exception {
Map<String, Object> listData = Json.parseSafe(api("file/sort?" + this.pr + "&pdir_fid=" + this.saveDirId + "&_page=1&_size=200&_sort=file_type:asc,updated_at:desc", Collections.emptyMap(), Collections.emptyMap(), 0, "GET"), Map.class);
if (listData.get("data") != null) {
List<Map<String, Object>> fileList = (List<Map<String, Object>>) ((Map<String, Object>) listData.get("data")).get("list");
if (fileList.size() >= 10) {
List<String> fileIdsToDelete = new ArrayList<>();
for (Map<String, Object> file : fileList) {
fileIdsToDelete.add((String) file.get("fid"));
}
api("file/delete?" + this.pr + "&uc_param_str=", Collections.emptyMap(), Map.of("action_type", 2, "filelist", fileIdsToDelete, "exclude_fids", Collections.emptyList()), 0, "POST");
Map<String, Object> listData = Json.parseSafe(api("file/sort?" + this.pr + "&pdir_fid=" + this.saveDirId + "&_page=1&_size=200&_sort=file_type:asc,updated_at:desc", Collections.emptyMap(), Collections.emptyMap(), 0, "GET"), Map.class);
if (listData.get("data") != null && ((List<Map<String, Object>>) ((Map<String, Object>) listData.get("data")).get("list")).size() > 0) {
List<String> list = new ArrayList<>();
for (Map<String, Object> stringStringMap : ((List<Map<String, Object>>) ((Map<String, Object>) listData.get("data")).get("list"))) {
list.add((String) stringStringMap.get("fid"));
}
api("file/delete?" + this.pr, Collections.emptyMap(), Map.of("action_type", "2", "filelist", Json.toJson(list), "exclude_fids", ""), 0, "POST");
}
}

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_CONTAIN = "cloud.189.cn/";
public static final String URL_START = "https://cloud.189.cn/";
private Map<String, JsonObject> shareTokenCache = new HashMap<>();
@ -175,7 +175,7 @@ public class TianyiApi {
header.put("Cookie", cookieJar.loadForRequest("https://cloud.189.cn/api/portal/getNewVlcVideoPlayUrl.action"));
return Result.get().url(ProxyServer.INSTANCE.buildProxyUrl(playUrl, header)).octet().header(header).string();
return Result.get().url(ProxyVideo.buildCommonProxyUrl(playUrl, header)).octet().header(header).string();
}
@ -221,30 +221,41 @@ public class TianyiApi {
public ShareData getShareData(String url, String accessCode) {
// 从整个URL中直接提取访问码无论格式如何
Matcher accessMatcher = Pattern.compile("访问码[:]([a-zA-Z0-9]+)").matcher(url);
if (accessMatcher.find()) {
accessCode = accessMatcher.group(1);
} else if (accessCode == null || accessCode.isEmpty()) {
accessCode = "";
}
String shareCode = null;
// 第一种匹配规则兼容http和https
Matcher matcher = Pattern.compile("https?:\\/\\/cloud\\.189\\.cn\\/web\\/share\\?code=([^&\\s]+)").matcher(url);
String shareCode = "";
// 第一种匹配规则使用预编译的 regex
Matcher matcher = Pattern.compile("https:\\/\\/cloud\\.189\\.cn\\/web\\/share\\?code=([^&]+)").matcher(url);
if (matcher.find() && matcher.group(1) != null) {
shareCode = matcher.group(1);
// 从shareCode中提取访问码
Matcher accessMatcher = Pattern.compile("访问码:([a-zA-Z0-9]+)").matcher(shareCode);
if (accessMatcher.find()) {
accessCode = accessMatcher.group(1);
} else {
// 第二种匹配规则直接匹配 cloud.189.cn/t/ 格式兼容http和https匹配到空格前
Matcher fallbackMatcher = Pattern.compile("https?:\\/\\/cloud\\.189\\.cn\\/t/([^\\s]+)").matcher(url);
accessCode = "";
}
} else {
// 第二种匹配规则直接匹配 cloud.189.cn/t/ 格式
Matcher fallbackMatcher = Pattern.compile("https://cloud\\.189\\.cn/t/([^&]+)").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&sys=darwin&ve=1.8.6&ut=Nk27FcCv6q1eo6rXz8QHR/nIG6qLA3jh7KdL+agFgcOvww==";
private String pr = "pr=UCBrowser&fr=pc";
private List<String> subtitleExts = Arrays.asList(".srt", ".ass", ".scc", ".stl", ".ttml");
private Map<String, String> saveFileIdCaches = new HashMap<>();
private String saveDirId = null;
@ -87,15 +87,7 @@ public class UCApi {
cache = Cache.objectFrom(Path.read(getCache()));
tokenCache = Cache.objectFrom(Path.read(qrCodeHandler.getCache()));
java.lang.String tokenCacheJson = tokenCache.getUser().getCookie();
if (StringUtils.isNoneBlank(tokenCacheJson)) {
//刷新token,并返回
this.cookieToken = qrCodeHandler.refreshToken(Json.safeObject(tokenCacheJson).getAsJsonObject().get("refresh_token").getAsString());
SpiderDebug.log("UC初始化获取到的cookieToken: " + cookieToken);
}
this.cookieToken = tokenCache.getUser().getCookie();
SpiderDebug.log("UC初始化获取到的cookieToken: " + cookieToken);
}
@ -189,9 +181,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()) {
@ -225,19 +217,23 @@ 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");
if (flag.contains("uc原画")) {
playUrl = this.getDownload(shareId, stoken, fileId, fileToken, true);
//UCTV 可以直接播放不需要代理
if (testVideo(playUrl)) {
SpiderDebug.log("UCTV 可以直接播放,不需要代理" );
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,7 +178,25 @@ public class UCTokenHandler {
JsonObject resData = Json.safeObject(okResult.getBody());
String code = resData.get("code").getAsString();
OkResult okResult1 = getAccessToken(code, false);
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);
if (okResult1.getCode() == 200) {
@ -190,7 +208,8 @@ public class UCTokenHandler {
SpiderDebug.log("uc Token获取成功" + tokenResData.get("data").getAsJsonObject().get("access_token").getAsString());
//保存到本地
cache.setTokenUser(User.objectFrom(Json.toJson(tokenResData.get("data").getAsJsonObject())));
cache.setTokenUser(User.objectFrom(tokenResData.get("data").getAsJsonObject().get("access_token").getAsString()));
//停止检验线程关闭弹窗
stopService();
return result;
@ -207,74 +226,6 @@ 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);
@ -315,11 +266,6 @@ 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

@ -1,39 +1,25 @@
package com.github.catvod.api;
import androidx.annotation.NonNull;
import com.github.catvod.bean.Result;
import com.github.catvod.crawler.SpiderDebug;
import com.github.catvod.net.OkHttp;
import com.github.catvod.net.OkResult;
import com.github.catvod.utils.Json;
import com.github.catvod.utils.ProxyServer;
import com.github.catvod.utils.ProxyVideo;
import com.github.catvod.utils.Util;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.nio.charset.Charset;
import java.security.GeneralSecurityException;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class YunDrive {
private final Pattern regex = Pattern.compile("https://yun\\.139\\.com/shareweb/#/w/i/([^&]+)");
private final SecretKeySpec secretKey;
@ -90,14 +76,6 @@ public class YunDrive {
matcher = Pattern.compile("https://caiyun\\.139\\.com/m/i\\?([^&]+)").matcher(url);
finded = matcher.find();
}
if (!finded) {
matcher = Pattern.compile("https://yun.139.com/shareweb/#/w/i/([\\w-]+)").matcher(url);
finded = matcher.find();
}
if (!finded) {
matcher = Pattern.compile("https://caiyun.139.com/w/i/([\\w-]+)").matcher(url);
finded = matcher.find();
}
if (finded) linkID = matcher.group(1);
return linkID;
@ -191,7 +169,7 @@ public class YunDrive {
for (JsonElement element : response.getAsJsonArray("coLst")) {
JsonObject entry = element.getAsJsonObject();
if (entry.get("coType").getAsInt() == 3) {
items.add(Map.of("name", entry.get("coName").getAsString(), "contentId", entry.get("coID").getAsString(), "linkID", linkID, "path", entry.get("path").getAsString()));
items.add(Map.of("name", entry.get("coName").getAsString(), "contentId", entry.get("coID").getAsString(), "linkID", linkID));
}
}
} else if (response.has("caLst")) {
@ -220,96 +198,5 @@ public class YunDrive {
return m3u8.split("playlist.m3u8")[0]+resultUrl;
}
public String get4kVideoInfo(String fid, String linkID) throws Exception {
String auth = getAuth();
String phone = getPhone();
// 构建 JSON 请求体
Map<String, Object> requestBody = new HashMap<>();
Map<String, Object> dlFromOutLinkReqV3 = new HashMap<>();
Map<String, Object> commonAccountInfo = new HashMap<>();
dlFromOutLinkReqV3.put("linkID", linkID);
dlFromOutLinkReqV3.put("account", phone);
Map<String, Object> coIDLst = new HashMap<>();
coIDLst.put("item", Collections.singletonList(fid));
dlFromOutLinkReqV3.put("coIDLst", coIDLst);
commonAccountInfo.put("account", phone);
commonAccountInfo.put("accountType", 1);
requestBody.put("dlFromOutLinkReqV3", dlFromOutLinkReqV3);
requestBody.put("commonAccountInfo", commonAccountInfo);
/* {
"dlFromOutLinkReqV3" : {
"linkID" : "105CpbaJQFYc6",
"account" : "18896781601",
"coIDLst" : {
"item" : [ "DFTOdJkuAEwA1011ZpAcj1pl039202404112124392cb/Fkco6TgMKlnJwKbVul0ZKeYT5p2hIioVy" ]
}
},
"commonAccountInfo" : {
"account" : "18896781601",
"accountType" : 1
}
}*/
// 构建请求
Map<String, String> header = getHeader();
OkResult okResult = OkHttp.post(baseUrl + "dlFromOutLinkV3", encrypt(Json.toJson(requestBody)), header);
JsonObject resultJson = Json.safeObject(decrypt(okResult.getBody()));
if (resultJson.get("resultCode").getAsInt() == 0) {
return resultJson.getAsJsonObject("data").get("redrUrl").getAsString();
}
// 解析 JSON 响应
return null;
}
private static String getPhone() {
String phone = StringUtils.split(Util.base64Decode(getAuth()), ":")[1];
SpiderDebug.log("phone:" + phone);
return phone;
}
private static String getAuth() {
String auth = YunTokenHandler.get().getToken();
SpiderDebug.log("auth:" + auth);
return auth;
}
@NonNull
private static Map<String, String> getHeader() {
Map<String, String> header = new HashMap<>();
header.put("X-Deviceinfo", "||3|12.27.0|safari|13.1.2|1||macos 10.15.6|1324X381|zh-cn|||");
header.put("hcy-cool-flag", "1");
header.put("Authorization", "Basic " + getAuth());
header.put("Content-Type", "application/json");
return header;
}
public String playerContent(String[] split, String flag) throws Exception {
String playUrl = "";
if (flag.contains("原画")) {
String contentId = split[0];
String linkID = split[1];
playUrl = YunDrive.get().get4kVideoInfo(contentId, linkID);
playUrl = ProxyServer.INSTANCE.buildProxyUrl(playUrl, new HashMap<>());
} else {
String contentId = split[0];
String linkID = split[1];
playUrl = YunDrive.get().fetchPlayUrl(contentId, linkID);
}
return Result.get().url(playUrl).octet().header(getHeader()).string();
}
}

View File

@ -1,36 +0,0 @@
package com.github.catvod.api;
import com.github.catvod.bean.yun.Cache;
import com.github.catvod.bean.yun.User;
import com.github.catvod.utils.Path;
import java.io.File;
public class YunTokenHandler {
private final Cache cache;
public File getCache() {
return Path.tv("yun139");
}
private YunTokenHandler() {
cache = Cache.objectFrom(Path.read(getCache()));
}
private static class Loader {
static volatile YunTokenHandler INSTANCE = new YunTokenHandler();
}
public static YunTokenHandler get() {
return YunTokenHandler.Loader.INSTANCE;
}
public String getToken() {
User user = cache.getUser();
return user.getCookie();
//return "cGM6MTg4OTY3ODE2MDE6eTM1Tjd1dG58MXxSQ1N8MTc1NDQ2OTgwNzEyOXxzMlN0T1VEV3lOVmF5V3pNbGFfM2tJbVp1ZmlqSHBqaEhTSzVyNHZqVXNRLmlhV3loSUxHNDFkMUI5N1BqXzhWN0dtVWtKLnBTclhpNGpZU1EuTGZWMTV3MVFoZmNpcEVoZkxUV2tvYjB0bkFTYV9RTUhhaHhveWx6YkdmcEhQdjNCS1lrbnp1LkxaWDdKOE40YkNNRjkzT3piNmx2Y0d3TWdVUkl5b18ubVUt";
}
}

View File

@ -1,40 +0,0 @@
package com.github.catvod.bean.BD;
import com.github.catvod.api.BaiDuYunHandler;
import com.github.catvod.bean.yun.User;
import com.github.catvod.spider.Init;
import com.github.catvod.utils.Path;
import com.google.gson.Gson;
import com.google.gson.annotations.SerializedName;
public class Cache {
@SerializedName("user")
private User user;
public static Cache objectFrom(String str) {
Cache item = new Gson().fromJson(str, Cache.class);
return item == null ? new Cache() : item;
}
public User getUser() {
return user == null ? new User("") : user;
}
public void setUser(User user) {
this.user = user;
this.save();
}
public void save() {
Init.execute(() -> Path.write(BaiDuYunHandler.get().getCache(), toString()));
}
@Override
public String toString() {
return new Gson().toJson(this);
}
}

View File

@ -6,16 +6,13 @@ import com.github.catvod.bean.Vod;
import com.github.catvod.utils.Util;
import com.google.gson.annotations.SerializedName;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Data {
@SerializedName("jump_id")
@SerializedName(value = "jump_id", alternate = "id")
private String jumpId;
@SerializedName("id")
private String id;
@SerializedName(value = "thumbnail", alternate = "path")
private String thumbnail;
@SerializedName("title")
@ -27,30 +24,24 @@ public class Data {
@SerializedName("playlist")
private Value playlist;
@SerializedName("year")
private String year;
private Value year;
@SerializedName("area")
private String area;
private Value area;
@SerializedName("types")
private List<Value> types;
@SerializedName("actors")
private List<Value> actors;
@SerializedName("directors")
private List<Value> directors;
@SerializedName("source_list_source")
private List<SourceListSource> source;
@SerializedName("dataList")
private List<Data> dataList;
@SerializedName("btbo_downlist")
private List<BtboDown> btboDownlist;
public String getJumpId() {
return TextUtils.isEmpty(jumpId) ? "" : jumpId;
}
public String getId() {
return TextUtils.isEmpty(id) ? "" : id;
}
public String getThumbnail(String imgDomain) {
return TextUtils.isEmpty(thumbnail) ? "" : "http://" + imgDomain + thumbnail;
public String getThumbnail() {
return TextUtils.isEmpty(thumbnail) ? "" : thumbnail + "@Referer=www.jianpianapp.com@User-Agent=jianpian-version362";
}
public String getTitle() {
@ -70,11 +61,11 @@ public class Data {
}
public String getYear() {
return year == null ? "" : year;
return year == null ? "" : year.getTitle();
}
public String getArea() {
return area == null ? "" : area;
return area == null ? "" : area.getTitle();
}
public String getTypes() {
@ -89,20 +80,12 @@ public class Data {
return directors == null ? "" : getValues(directors, true);
}
public List<SourceListSource> getSource() {
return source == null ? Collections.emptyList() : source;
public List<BtboDown> getBtboDownlist() {
return btboDownlist == null ? Collections.emptyList() : btboDownlist;
}
public List<Data> getDataList() {
return dataList == null ? Collections.emptyList() : dataList;
}
public Vod homeVod(String imgDomain) {
return new Vod(getJumpId(), getTitle(), getThumbnail(imgDomain));
}
public Vod vod(String imgDomain) {
return new Vod(getId(), getTitle(), getThumbnail(imgDomain), getMask());
public Vod vod() {
return new Vod(getJumpId(), getTitle(), getThumbnail(), getMask());
}
public String getValues(List<Value> items, boolean link) {
@ -111,6 +94,12 @@ public class Data {
return Util.substring(sb.toString());
}
public String getPlayUrl() {
StringBuilder sb = new StringBuilder();
for (BtboDown value : getBtboDownlist()) sb.append(value.getVal()).append("#");
return Util.substring(sb.toString());
}
public static class Value {
@SerializedName(value = "title", alternate = "name")
@ -129,51 +118,13 @@ public class Data {
}
}
public static class SourceListSource {
public static class BtboDown {
@SerializedName("name")
private String name;
@SerializedName("source_list")
private List<SourceList> list;
@SerializedName("val")
private String val;
public String getName() {
return TextUtils.isEmpty(name) ? "" : name;
}
public List<SourceList> getList() {
return list == null ? Collections.emptyList() : list;
public String getVal() {
return TextUtils.isEmpty(val) ? "" : val.replaceAll("ftp", "tvbox-xg:ftp");
}
}
public static class SourceList {
@SerializedName("source_name")
private String name;
@SerializedName("url")
private String url;
public String getName() {
return TextUtils.isEmpty(name) ? "" : name;
}
public String getUrl() {
return TextUtils.isEmpty(url) ? "" : url.replaceAll("ftp", "tvbox-xg:ftp");
}
}
public String getVodFrom() {
List<String> items = new ArrayList<>();
for (SourceListSource source : getSource()) items.add(source.getName());
return TextUtils.join("$$$", items);
}
public String getVodUrl() {
List<String> items = new ArrayList<>();
for (SourceListSource source : getSource()) {
List<String> urls = new ArrayList<>();
for (SourceList item : source.getList()) urls.add(item.getName() + "$" + item.getUrl());
items.add(TextUtils.join("#", urls));
}
return TextUtils.join("$$$", items);
}
}

View File

@ -1,52 +0,0 @@
package com.github.catvod.bean.jianpian;
import android.text.TextUtils;
import com.github.catvod.bean.Vod;
import com.google.gson.Gson;
import com.google.gson.annotations.SerializedName;
import java.util.Collections;
import java.util.List;
public class Search {
@SerializedName("data")
private List<Search> data;
@SerializedName("id")
private String id;
@SerializedName(value = "thumbnail", alternate = "path")
private String thumbnail;
@SerializedName("title")
private String title;
@SerializedName("mask")
private String mask;
public static Search objectFrom(String str) {
return new Gson().fromJson(str, Search.class);
}
public String getId() {
return TextUtils.isEmpty(id) ? "" : id;
}
public String getThumbnail(String imgDomain) {
return TextUtils.isEmpty(thumbnail) ? "" : "http://" + imgDomain + thumbnail;
}
public String getTitle() {
return TextUtils.isEmpty(title) ? "" : title;
}
public String getMask() {
return TextUtils.isEmpty(mask) ? "" : mask;
}
public Vod vod(String imgDomain) {
return new Vod(getId(), getTitle(), getThumbnail(imgDomain), getMask());
}
public List<Search> getData() {
return data == null ? Collections.emptyList() : data;
}
}

View File

@ -1,46 +0,0 @@
package com.github.catvod.bean.pan123;
import com.github.catvod.api.Pan123Handler;
import com.github.catvod.crawler.SpiderDebug;
import com.github.catvod.spider.Init;
import com.github.catvod.utils.Path;
import com.google.gson.Gson;
import com.google.gson.annotations.SerializedName;
public class Cache {
@SerializedName("user")
private User user;
public static Cache objectFrom(String str) {
if(str.isEmpty()) return new Cache();
SpiderDebug.log("Cache.objectFrom: " + str);
Cache item = new Gson().fromJson(str, Cache.class);
return item == null ? new Cache() : item;
}
public User getUser() {
return user == null ? new User() : user;
}
public void setUserInfo(User user) {
this.user = user;
this.saveUser();
}
public void saveUser() {
SpiderDebug.log("Cache.saveUser: " + toString());
SpiderDebug.log("Cache.path: " + Pan123Handler.INSTANCE.getCache().getAbsolutePath());
Init.execute(() -> Path.write(Pan123Handler.INSTANCE.getCache(), toString()));
}
@Override
public String toString() {
return new Gson().toJson(this);
}
}

View File

@ -1,12 +0,0 @@
package com.github.catvod.bean.pan123
data class ShareInfo(
val filename: String,
val shareKey: String,
val sharePwd: String,
val next: Int,
val fileId: Long,
val S3KeyFlag: String,
val Size: Long,
val Etag: String
)

View File

@ -1,63 +0,0 @@
package com.github.catvod.bean.pan123;
import com.google.gson.Gson;
import com.google.gson.annotations.SerializedName;
public class User {
@SerializedName("expire")
private long expire;
@SerializedName("cookie")
private String cookie;
@SerializedName("userName")
private String userName;
@SerializedName("password")
private String password;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getCookie() {
return cookie;
}
public void setCookie(String cookie) {
this.cookie = cookie;
}
public long getExpire() {
return expire;
}
public void setExpire(long expire) {
this.expire = expire;
}
public static User objectFrom(String str) {
User item = new Gson().fromJson(str, User.class);
return item == null ? new User() : item;
}
public void clean() {
this.cookie = "";
this.userName = "";
this.password = "";
this.expire = 0L;
}
}

View File

@ -1,39 +0,0 @@
package com.github.catvod.bean.yun;
import com.github.catvod.api.YunTokenHandler;
import com.github.catvod.spider.Init;
import com.github.catvod.utils.Path;
import com.google.gson.Gson;
import com.google.gson.annotations.SerializedName;
public class Cache {
@SerializedName("user")
private User user;
public static Cache objectFrom(String str) {
Cache item = new Gson().fromJson(str, Cache.class);
return item == null ? new Cache() : item;
}
public User getUser() {
return user == null ? new User("") : user;
}
public void setUser(User user) {
this.user = user;
this.save();
}
public void save() {
Init.execute(() -> Path.write(YunTokenHandler.get().getCache(), toString()));
}
@Override
public String toString() {
return new Gson().toJson(this);
}
}

View File

@ -1,30 +0,0 @@
package com.github.catvod.bean.yun;
import com.google.gson.annotations.SerializedName;
public class User {
public User(String cookie) {
this.cookie = cookie;
}
@SerializedName("cookie")
private String cookie;
public String getCookie() {
return cookie;
}
public void setCookie(String cookie) {
this.cookie = cookie;
}
public static User objectFrom(String cookie) {
return new User(cookie);
}
public void clean() {
this.cookie = "";
}
}

View File

@ -1,107 +0,0 @@
package com.github.catvod.spider
import android.content.Context
import android.text.TextUtils
import com.github.catvod.api.BaiduDrive
import com.github.catvod.api.BaiduDrive.getPlayFormatList
import com.github.catvod.api.BaiduDrive.getVod
import com.github.catvod.api.BaiduDrive.playerContent
import com.github.catvod.api.BaiduDrive.setCookie
import com.github.catvod.bean.Result
import com.github.catvod.bean.Vod
import com.github.catvod.crawler.Spider
import com.github.catvod.crawler.SpiderDebug
import com.github.catvod.utils.Json
import kotlinx.coroutines.runBlocking
import java.util.*
/**
* @author ColaMint & Adam & FongMi
*/
class BaiDuPan : Spider() {
@Throws(Exception::class)
override fun init(context: Context?, extend: String) {
setCookie(extend)
}
/**
* ids share_link
* @param ids
* @return
* @throws Exception
*/
@Throws(Exception::class)
override fun detailContent(ids: MutableList<String>): String? {
var vod: Vod? = null;
runBlocking {
vod = getVod(ids[0])
}
return Result.string(vod);
}
@Throws(Exception::class)
override fun playerContent(flag: String, id: String, vipFlags: MutableList<String?>?): String {
var content = ""
runBlocking {
content = playerContent(Json.safeObject(com.github.catvod.utils.Util.base64Decode(id)), flag)
}
return content
}
/**
* 獲取詳情內容視頻播放來源 shared_link
*
* @param ids share_link 集合
* @param i
* @return 詳情內容視頻播放來源
*/
fun detailContentVodPlayFrom(ids: MutableList<String?>, index: Int): String? {
val playFrom: MutableList<String?> = ArrayList<String?>()
/* if (ids.size() < 2){
return TextUtils.join("$$$", BaiduDrive.get().getPlayFormatList());
}*/
for (i in 1..ids.size) {
playFrom.add( String.format(Locale.getDefault(), "BD原画" + "#%02d_%02d", i, index))
/* for (s in getPlayFormatList()) {
playFrom.add(String.format(Locale.getDefault(), "BD" + s + "#%02d%02d", i, index))
}*/
}
return TextUtils.join("$$$", playFrom)
}
/**
* 獲取詳情內容視頻播放地址 share_link
*
* @param ids share_link 集合
* @return 詳情內容視頻播放地址
*/
fun detailContentVodPlayUrl(ids: List<String>): String? {
val playUrl: MutableList<String?> = ArrayList<String?>()
for (id in ids) {
try {
playUrl.add(getVod(id).getVodPlayUrl())
} catch (e: Exception) {
SpiderDebug.log("获取播放地址出错:" + e.message)
playUrl.add("")
}
}
return TextUtils.join("$$$", playUrl)
}
companion object {
@JvmField
var URL_START = "https://pan.baidu.com"
@Throws(Exception::class)
fun proxy(params: MutableMap<String, String>): Array<Any> {
val type = params["type"]
if ("video" == type) return BaiduDrive.proxyVideo(params)
//if ("sub".equals(type)) return AliYun.get().proxySub(params);
return arrayOf<Any>()
}
}
}

View File

@ -2,84 +2,63 @@ 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.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import static com.github.catvod.api.TianyiApi.URL_CONTAIN;
import static com.github.catvod.api.TianyiApi.URL_START;
/**
* @author ColaMint & Adam & FongMi
*/
public class Cloud extends Spider {
private Quark quark = null;
/* private Ali ali = 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();*/
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() : "");*/
ali.init(context, first && ext.has("token") ? ext.get("token").getAsString() : "");
tianYi.init(context, first && ext.has("tianyicookie") ? ext.get("tianyicookie").getAsString() : "");
yiDongYun.init(context, "");
baiDuPan.init(context, "");
pan123.init(context, "");
}
@Override
public String detailContent(List<String> shareUrl) throws Exception {
SpiderDebug.log("cloud detailContent shareUrl" + Json.toJson(shareUrl));
/* if (shareUrl.get(0).matches(Util.patternAli)) {
if (shareUrl.get(0).matches(Util.patternAli)) {
return ali.detailContent(shareUrl);
} else */
if (shareUrl.get(0).matches(Util.patternQuark)) {
} else if (shareUrl.get(0).matches(Util.patternQuark)) {
return quark.detailContent(shareUrl);
} else if (shareUrl.get(0).matches(Util.patternUC)) {
return uc.detailContent(shareUrl);
} else if (shareUrl.get(0).contains(URL_CONTAIN)) {
} else if (shareUrl.get(0).startsWith(TianyiApi.URL_START)) {
return tianYi.detailContent(shareUrl);
} else if (shareUrl.get(0).contains(YiDongYun.URL_START)) {
return yiDongYun.detailContent(shareUrl);
} else if (shareUrl.get(0).contains(BaiDuPan.URL_START)) {
return baiDuPan.detailContent(shareUrl);
} else if (shareUrl.get(0).matches(Pan123Api.regex)) {
SpiderDebug.log("Pan123Api shareUrl" + Json.toJson(shareUrl));
return pan123.detailContent(shareUrl);
}
return null;
}
@Override
public String playerContent(String flag, String id, List<String> vipFlags) throws Exception {
SpiderDebug.log("cloud playerContent flag" + flag + " id" + id);
if (flag.contains("quark")) {
return quark.playerContent(flag, id, vipFlags);
} else if (flag.contains("uc")) {
@ -88,105 +67,47 @@ public class Cloud extends Spider {
return tianYi.playerContent(flag, id, vipFlags);
} else if (flag.contains("移动")) {
return yiDongYun.playerContent(flag, id, vipFlags);
}/* else {
} else {
return ali.playerContent(flag, id, vipFlags);
}*/ else if (flag.contains("BD")) {
return baiDuPan.playerContent(flag, id, vipFlags);
} else if (flag.contains("pan123")) {
return pan123.playerContent(flag, id, vipFlags);
}
return flag;
}
protected String detailContentVodPlayFrom(List<String> shareLinks) {
ImmutablePair<List<String>, List<String>> pairs = resultMap.get(Util.MD5(Json.toJson(shareLinks)));
if (pairs != null && pairs.left != null && !pairs.left.isEmpty()) {
return TextUtils.join("$$$", pairs.right);
}
getPlayFromAndUrl(shareLinks);
pairs = resultMap.get(Util.MD5(Json.toJson(shareLinks)));
if (pairs != null && pairs.left != null && !pairs.left.isEmpty()) {
return TextUtils.join("$$$", pairs.right);
}
return "";
}
protected String detailContentVodPlayUrl(List<String> shareLinks) {
ImmutablePair<List<String>, List<String>> pairs = resultMap.get(Util.MD5(Json.toJson(shareLinks)));
if (pairs != null && pairs.left != null && !pairs.left.isEmpty()) {
return TextUtils.join("$$$", pairs.left);
}
getPlayFromAndUrl(shareLinks);
pairs = resultMap.get(Util.MD5(Json.toJson(shareLinks)));
if (pairs != null && pairs.left != null && !pairs.left.isEmpty()) {
return TextUtils.join("$$$", pairs.left);
}
return "";
}
//同時获取from 和url 放入缓存只要一个函数执行就行避免重复执行
private void getPlayFromAndUrl(List<String> shareLinks) {
ExecutorService service = Executors.newFixedThreadPool(4);
try { //首先清空缓存避免太多缓存
resultMap.clear();
List<String> urls = new ArrayList<>();
List<String> froms = new ArrayList<>();
List<Future<ImmutablePair<String, String>>> futures = new ArrayList<>();
List<String> from = new ArrayList<>();
int i = 0;
for (String shareLink : shareLinks) {
int finalI = ++i;
futures.add(service.submit(() -> {
String url = "";
String from = "";
i++;
if (shareLink.matches(Util.patternUC)) {
url = uc.detailContentVodPlayUrl(List.of(shareLink));
from = uc.detailContentVodPlayFrom(List.of(shareLink), finalI);
from.add(uc.detailContentVodPlayFrom(List.of(shareLink), i));
} 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);
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)) {
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);
from.add(yiDongYun.detailContentVodPlayFrom(List.of(shareLink), i));
}
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);
return TextUtils.join("$$$", from);
}
}
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();
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)));
}
}
return TextUtils.join("$$$", urls);
}
}

View File

@ -1,180 +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.bean.Vod.VodPlayBuilder
import com.github.catvod.net.OkHttp
import com.github.catvod.utils.ProxyVideo
import com.github.catvod.utils.Util
import org.jsoup.Jsoup
import org.jsoup.nodes.Document
import org.jsoup.select.Elements
import java.net.URLEncoder
class DiDa : Cloud() {
private val headers: HashMap<String?, String?>
get() {
val headers = java.util.HashMap<String?, String?>()
headers.put("User-Agent", Util.CHROME)
return headers
}
private fun parseVodFromDoc(doc: Document): MutableList<Vod?> {
val list: MutableList<Vod?> = ArrayList<Vod?>()
for (element in doc.select("div.myui-vodlist__box")) {
var pic = element.selectFirst("img")?.attr("src")
if (pic.isNullOrBlank()) {
pic = element.selectFirst("a")?.attr("data-original")
}
val url = element.selectFirst("a")?.attr("href")
val name = element.select("h4 > a").text()
list.add(Vod(url, name, pic))
}
return list
}
@Throws(Exception::class)
override fun homeContent(filter: Boolean): String? {
val doc = Jsoup.parse(
OkHttp.string(
siteUrl, this.headers
)
)
var classes: List<Class?>
val menuList = doc.select("ul.nav-menu > li > a")
classes = menuList.map {
val url = it.attr("href")
val title = it.text()
Class(url, title)
}
val list: MutableList<Vod?> = parseVodFromDoc(doc)
return Result.string(classes, list)
}
@Throws(Exception::class)
override fun categoryContent(
tid: String, pg: String, filter: Boolean, extend: java.util.HashMap<String?, String?>?
): String? {
val type = tid.replace("/type/", "").replace(".html", "")
val target: String = siteUrl + "/show/${type}--------${pg}---.html"
val doc = Jsoup.parse(OkHttp.string(target, this.headers))
val list = parseVodFromDoc(doc)
val total = (pg.toInt() + 1) * 24
return Result.get().vod(list).page(pg.toInt(), pg.toInt() + 1, 48, total).string()
}
@Throws(Exception::class)
override fun detailContent(ids: MutableList<String?>): String? {
val doc = Jsoup.parse(
OkHttp.string(
siteUrl + ids.get(0), this.headers
)
)
val name = doc.select("h1").text()
val img = doc.select("a.myui-vodlist__thumb > img")
val pic = img.attr("src") ?: img.attr("data-original")
val desc = doc.select("p.data").text()
val year = Util.findByRegex("年份:(.*)又名", desc, 1).trim()
val area = Util.findByRegex("地区:(.*)语言", desc, 1).trim()
val actor = Util.findByRegex("主演:(.*)导演", desc, 1).trim()
val builder = VodPlayBuilder()
val playFromTab = doc.selectFirst("div.myui-panel__head > ul.nav ")
val playFromEles = playFromTab?.select("ul > li > a") ?: Elements()
for (ele in playFromEles) {
val playUrlList = mutableListOf<Vod.VodPlayBuilder.PlayUrl>()
val id = ele.attr("href")
val playFrom = ele.text()
if (playFrom.contains("网盘")) {
continue
}
val playElement = doc.select("$id")
for (element in playElement.select("ul > li > a")) {
val playUrl = VodPlayBuilder.PlayUrl()
playUrl.url = element.attr("href")
playUrl.name = element.text()
playUrlList.add(playUrl)
}
builder.append(playFrom, playUrlList)
}
var panFrom = ""
var panURl = ""
for (element in doc.select(" div.myui-panel_bd.clearfix > p > a")) {
if (element.attr("href").matches(Util.patternQuark.toRegex())) {
panFrom = super.detailContentVodPlayFrom(listOf<String>(element.attr("href")))
panURl = super.detailContentVodPlayUrl(listOf<String>(element.attr("href")))
}
}
val result = builder.build()
val vod = Vod()
vod.setVodId(ids[0])
vod.setVodPic(ProxyVideo.buildCommonProxyUrl(pic, Util.webHeaders(pic)))
vod.setVodYear(year)
vod.setVodActor(actor)
vod.setVodArea(area)
vod.setVodName(name)
vod.setVodPlayFrom(result.vodPlayFrom + "$$$" + panFrom)
vod.setVodPlayUrl(result.vodPlayUrl + "$$$" + panURl)
return Result.string(vod)
}
@Throws(Exception::class)
override fun searchContent(key: String?, quick: Boolean): String? {
val doc = Jsoup.parse(
OkHttp.string(
searchUrl + URLEncoder.encode(key), this.headers
)
)
val list: MutableList<Vod?> = ArrayList<Vod?>()
for (element in doc.select("a.cover-link")) {
val pic = element.select("img").attr("data-src")
val url = element.attr("href")
val name = element.select("img").attr("alt")
val id: String? = url.split("/".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()[2]
list.add(Vod(id, name, ProxyVideo.buildCommonProxyUrl(pic, Util.webHeaders(pic))))
}
return Result.string(list)
}
@Throws(Exception::class)
override fun playerContent(flag: String?, id: String?, vipFlags: MutableList<String?>?): String? {
if (flag == null) {
return Result.get().url("").header(this.headers).string()
} else if (flag.contains("quark")) {
super.playerContent(flag, id, vipFlags)
} else {
}
return Result.get().url(id).header(this.headers).string()
}
companion object {
private const val siteUrl = "https://www.didahd.pro"
private val searchUrl: String = siteUrl + "/search?q="
}
}

View File

@ -10,7 +10,6 @@ import android.os.Handler;
import android.os.Looper;
import com.github.catvod.crawler.SpiderDebug;
import com.github.catvod.utils.ProxyServer;
import java.lang.reflect.Field;
import java.util.Map;
@ -42,11 +41,7 @@ public class Init {
public static void init(Context context) {
get().app = ((Application) context);
SpiderDebug.log("自定義爬蟲代碼載入成功!" + "1");
execute(() -> {
ProxyServer.INSTANCE.stop();
ProxyServer.INSTANCE.start();
});
SpiderDebug.log("自定義爬蟲代碼載入成功!");
}
public static void execute(Runnable runnable) {
@ -65,8 +60,7 @@ public class Init {
try {
Activity activity = Init.getActivity();
if (activity == null || Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return;
if (activity.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED)
return;
if (activity.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) return;
activity.requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 9999);
} catch (Exception e) {
e.printStackTrace();

View File

@ -0,0 +1,107 @@
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

@ -0,0 +1,133 @@
package com.github.catvod.spider;
import android.content.Context;
import android.net.Uri;
import android.text.TextUtils;
import com.github.catvod.bean.Class;
import com.github.catvod.bean.Result;
import com.github.catvod.bean.Vod;
import com.github.catvod.crawler.Spider;
import com.github.catvod.net.OkHttp;
import com.github.catvod.utils.Util;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author Qile
*/
public class JavDb extends Spider {
private static String siteUrl = "https://javdb523.com";
@Override
public void init(Context context, String extend) throws Exception {
if (!extend.isEmpty()) siteUrl = extend;
}
private Map<String, String> getHeader() {
Map<String, String> header = new HashMap<>();
header.put("User-Agent", Util.CHROME);
header.put("Referer", siteUrl + "/");
return header;
}
@Override
public String homeContent(boolean filter) throws Exception {
List<Class> classes = new ArrayList<>();
List<String> typeIds = Arrays.asList("", "censored", "uncensored", "western");
List<String> typeNames = Arrays.asList("全部", "有码", "无码", "欧美");
for (int i = 0; i < typeIds.size(); i++) classes.add(new Class(typeIds.get(i), typeNames.get(i)));
Document doc = Jsoup.parse(OkHttp.string(siteUrl, getHeader()));
List<Vod> list = new ArrayList<>();
for (Element li : doc.select(".item")) {
String vid = siteUrl + li.select("a").attr("href");
String name = li.select("a").attr("title");
String pic = li.select("img").attr("src");
list.add(new Vod(vid, name, pic));
}
return Result.string(classes, list);
}
@Override
public String categoryContent(String tid, String pg, boolean filter, HashMap<String, String> extend) throws Exception {
String cateUrl = siteUrl + String.format("/%s?page=%s", tid, pg);
Document doc = Jsoup.parse(OkHttp.string(cateUrl, getHeader()));
List<Vod> list = new ArrayList<>();
for (Element li : doc.select(".item")) {
String vid = siteUrl + li.select("a").attr("href");
String name = li.select("a").attr("title");
String pic = li.select("img").attr("src");
list.add(new Vod(vid, name, pic));
}
return Result.string(list);
}
@Override
public String detailContent(List<String> ids) throws Exception {
Document doc = Jsoup.parse(OkHttp.string(ids.get(0), getHeader()));
if (doc.text().contains("歡迎登入")) return Result.error("该资源需要登入");
List<String> vodItems = new ArrayList<>();
Elements sourceList = doc.select(".item.columns");
for (Element a : sourceList) {
String episodeUrl = a.select("div a").attr("href");
String episodeName = a.select("div a").text();
vodItems.add(episodeName + "$" + episodeUrl);
}
Elements elements = doc.select(".panel-block");
String classifyName = "";
String year = "";
String area = "";
String remark = "";
for (Element element : elements) {
String text = element.text();
if (text.startsWith("類別:")) {
classifyName = element.select("span a").text();
} else if (text.startsWith("片商:")) {
area = element.select("span a").text();
} else if (text.startsWith("日期:")) {
year = element.select("span").text();
} else if (text.startsWith("時長:")) {
remark = element.select("span").text();
}
}
Vod vod = new Vod();
vod.setVodId(ids.get(0));
vod.setVodYear(year);
vod.setVodArea(area);
vod.setVodRemarks(remark);
vod.setTypeName(classifyName);
vod.setVodContent(ids.get(0));
vod.setVodPlayFrom("Qile");
vod.setVodPlayUrl(TextUtils.join("#", vodItems));
return Result.string(vod);
}
@Override
public String searchContent(String key, boolean quick) throws Exception {
String searchUrl = siteUrl + "/search?q=" + Uri.encode(key) + "&f=all";
Document doc = Jsoup.parse(OkHttp.string(searchUrl, getHeader()));
List<Vod> list = new ArrayList<>();
for (Element li : doc.select(".item")) {
String vid = siteUrl + li.select("a").attr("href");
String name = li.select("a").attr("title");
String pic = li.select("img").attr("src");
list.add(new Vod(vid, name, pic));
}
return Result.string(list);
}
@Override
public String playerContent(String flag, String id, List<String> vipFlags) throws Exception {
return Result.get().url(id).header(getHeader()).string();
}
}

View File

@ -1,114 +1,84 @@
package com.github.catvod.spider;
import android.content.Context;
import com.github.catvod.bean.Class;
import com.github.catvod.bean.Result;
import com.github.catvod.bean.Vod;
import com.github.catvod.bean.jianpian.Data;
import com.github.catvod.bean.jianpian.Detail;
import com.github.catvod.bean.jianpian.Resp;
import com.github.catvod.bean.jianpian.Search;
import com.github.catvod.crawler.Spider;
import com.github.catvod.crawler.SpiderDebug;
import com.github.catvod.net.OkHttp;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.github.catvod.utils.Json;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
/**
* Qile
*/
public class Jianpian extends Spider {
private String siteUrl = "https://ev5356.970xw.com";
private String imgDomain;
private final String siteUrl = "http://api2.rinhome.com";
private String extend;
private Map<String, String> getHeader() {
Map<String, String> headers = new HashMap<>();
headers.put("User-Agent", "Mozilla/5.0 (Linux; Android 9; V2196A Build/PQ3A.190705.08211809; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/91.0.4472.114 Mobile Safari/537.36;webank/h5face;webank/1.0;netType:NETWORK_WIFI;appVersion:416;packageName:com.jp3.xg3");
headers.put("Referer", siteUrl);
headers.put("User-Agent", "jianpian-android/360");
headers.put("JPAUTH", "y261ow7kF2dtzlxh1GS9EB8nbTxNmaK/QQIAjctlKiEv");
return headers;
}
@Override
public void init(Context context, String extend) throws Exception {
this.extend = extend;
JsonObject domains = new Gson().fromJson(OkHttp.string("https://dns.alidns.com/resolve?name=swrdsfeiujo25sw.cc&type=TXT"), JsonObject.class);
String parts = domains.getAsJsonArray("Answer").get(0).getAsJsonObject().get("data").getAsString();
parts = parts.replace("\"", "");
String[] domain = parts.split(",");
for (String d : domain) {
siteUrl = "https://wangerniu." + d;
String json = OkHttp.string(siteUrl + "/api/appAuthConfig");
if (!json.isEmpty()) {
JsonObject root = new Gson().fromJson(json, JsonObject.class);
imgDomain = root.getAsJsonObject("data").get("imgDomain").getAsString();
break;
}
}
}
@Override
public String homeContent(boolean filter) throws Exception {
List<Class> classes = new ArrayList<>();
List<String> typeIds = Arrays.asList("1", "2", "3", "4", "50", "99");
List<String> typeNames = Arrays.asList("電影", "電視劇", "動漫", "綜藝", "紀錄片", "Netflix");
List<String> typeIds = Arrays.asList("0", "1", "2", "3", "4");
List<String> typeNames = Arrays.asList("全部", "电影", "电视剧", "动漫", "综艺");
for (int i = 0; i < typeIds.size(); i++) classes.add(new Class(typeIds.get(i), typeNames.get(i)));
return Result.string(classes, JsonParser.parseString(OkHttp.string(extend)));
return Result.string(classes, Json.parse(OkHttp.string(extend)));
}
@Override
public String homeVideoContent() {
List<Vod> list = new ArrayList<>();
String url = siteUrl + "/api/slide/list?pos_id=88";
String url = siteUrl + "/api/slide/list?code=unknown9039b6856c3a3306&pos_id=888&channel=wandoujia";
Resp resp = Resp.objectFrom(OkHttp.string(url, getHeader()));
for (Data data : resp.getData()) list.add(data.homeVod(imgDomain));
for (Data data : resp.getData()) list.add(data.vod());
return Result.string(list);
}
@Override
public String categoryContent(String tid, String pg, boolean filter, HashMap<String, String> extend) throws Exception {
if (tid.endsWith("/{pg}")) return searchContent(tid.split("/")[0], pg);
if (tid.equals("50") || tid.equals("99") || tid.equals("111")) {
List<Vod> list = new ArrayList<>();
String url = siteUrl + String.format("/api/dyTag/list?category_id=%s&page=%s", tid, pg);
Resp resp = Resp.objectFrom(OkHttp.string(url, getHeader()));
for (Data data : resp.getData()) for (Data dataList : data.getDataList()) list.add(dataList.vod(imgDomain));
return Result.get().page().vod(list).string();
} else {
List<Vod> list = new ArrayList<>();
HashMap<String, String> ext = new HashMap<>();
if (extend != null && !extend.isEmpty()) ext.putAll(extend);
if (extend != null && extend.size() > 0) ext.putAll(extend);
String cateId = ext.get("cateId") == null ? tid : ext.get("cateId");
String area = ext.get("area") == null ? "0" : ext.get("area");
String year = ext.get("year") == null ? "0" : ext.get("year");
String by = ext.get("by") == null ? "updata" : ext.get("by");
String url = siteUrl + String.format("/api/crumb/list?fcate_pid=%s&area=%s&year=%s&type=0&sort=%s&page=%s&category_id=", tid, area, year, by, pg);
String by = ext.get("by") == null ? "hot" : ext.get("by");
String url = siteUrl + String.format("/api/crumb/list?area=%s&category_id=%s&page=%s&type=0&limit=24&sort=%s&year=%s", area, cateId, pg, by, year);
Resp resp = Resp.objectFrom(OkHttp.string(url, getHeader()));
for (Data data : resp.getData()) list.add(data.vod(imgDomain));
return Result.string(list);
}
for (Data data : resp.getData()) list.add(data.vod());
return Result.string(Integer.parseInt(pg), Integer.parseInt(pg) + 1, 24, Integer.MAX_VALUE, list);
}
@Override
public String detailContent(List<String> ids) throws Exception {
String url = siteUrl + "/api/video/detailv2?id=" + ids.get(0);
String url = siteUrl + "/api/node/detail?channel=wandoujia&token=&id=" + ids.get(0);
Data data = Detail.objectFrom(OkHttp.string(url, getHeader())).getData();
Vod vod = data.vod(imgDomain);
vod.setVodPlayFrom(data.getVodFrom());
Vod vod = data.vod();
vod.setVodPlayFrom("Jianpian");
vod.setVodYear(data.getYear());
vod.setVodArea(data.getArea());
vod.setTypeName(data.getTypes());
vod.setVodActor(data.getActors());
vod.setVodPlayUrl(data.getVodUrl());
vod.setVodPlayUrl(data.getPlayUrl());
vod.setVodDirector(data.getDirectors());
vod.setVodContent(data.getDescription());
return Result.string(vod);
@ -131,9 +101,9 @@ public class Jianpian extends Spider {
public String searchContent(String key, String pg) throws Exception {
List<Vod> list = new ArrayList<>();
String url = siteUrl + String.format("/api/v2/search/videoV2?key=%s&category_id=88&page=%s&pageSize=20", URLEncoder.encode(key), pg);
Search search = Search.objectFrom(OkHttp.string(url, getHeader()));
for (Search data : search.getData()) list.add(data.vod(imgDomain));
String url = siteUrl + "/api/video/search?page=" + pg + "&key=" + URLEncoder.encode(key);
Resp resp = Resp.objectFrom(OkHttp.string(url, getHeader()));
for (Data data : resp.getData()) list.add(data.vod());
return Result.string(list);
}
}

View File

@ -1,90 +0,0 @@
package com.github.catvod.spider;
import android.content.Context;
import android.text.TextUtils;
import com.github.catvod.api.Pan123Api;
import com.github.catvod.bean.Result;
import com.github.catvod.crawler.Spider;
import com.github.catvod.crawler.SpiderDebug;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
/**
* @author lushunming
*/
public class Pan123 extends Spider {
@Override
public void init(Context context, String extend) throws Exception {
if (StringUtils.isNoneBlank(extend)) {
// Pan123Api.INSTANCE.setAuth(extend);
}
}
@Override
public String detailContent(List<String> ids) throws Exception {
@NotNull Map<@NotNull String, @NotNull String> shareData = Pan123Api.INSTANCE.getShareData(ids.get(0));
return Result.string(Pan123Api.INSTANCE.getVod(shareData.get("key"), shareData.get("sharePwd")));
}
@Override
public String playerContent(String flag, String id, List<String> vipFlags) throws Exception {
return Pan123Api.INSTANCE.playerContent(id, flag);
}
/**
* 獲取詳情內容視頻播放來源 shared_link
*
* @param ids share_link 集合
* @param
* @return 詳情內容視頻播放來源
*/
public String detailContentVodPlayFrom(List<String> ids, int index) {
List<String> playFrom = new ArrayList<>();
/* if (ids.size() < 2){
return TextUtils.join("$$$", Pan123Api.INSTANCE.getPlayFormatList());
}*/
for (int i = 1; i <= ids.size(); i++) {
for (String s : Pan123Api.INSTANCE.getPlayFormatList()) {
playFrom.add(String.format(Locale.getDefault(), "pan123" + s + "#%02d_%02d", i, index));
}
// playFrom.add("天意" + i + index);
}
return TextUtils.join("$$$", playFrom);
}
/**
* 獲取詳情內容視頻播放地址 share_link
*
* @param ids share_link 集合
* @return 詳情內容視頻播放地址
*/
public String detailContentVodPlayUrl(List<String> ids){
List<String> playUrl = new ArrayList<>();
for (String id : ids) {
@NotNull Map<@NotNull String, @NotNull String> shareData = Pan123Api.INSTANCE.getShareData(id);
try {
playUrl.add(Pan123Api.INSTANCE.getVod(shareData.get("key"), shareData.get("sharePwd")).getVodPlayUrl());
} catch (Exception e) {
SpiderDebug.log("获取播放地址出错:" + e.getMessage());
playUrl.add("");
}
}
return TextUtils.join("$$$", playUrl);
}
}

View File

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

View File

@ -8,7 +8,6 @@ import com.github.catvod.bean.Result;
import com.github.catvod.bean.Sub;
import com.github.catvod.bean.Vod;
import com.github.catvod.crawler.Spider;
import com.github.catvod.crawler.SpiderDebug;
import com.github.catvod.net.OkHttp;
import com.github.catvod.utils.Image;
import com.github.catvod.utils.Util;
@ -20,54 +19,31 @@ import java.util.List;
public class Push extends Spider {
private Cloud cloud;
private final Ali ali;
public Push() {
cloud = new Cloud();
ali = new Ali();
}
@Override
public void init(Context context, String extend) {
try {
cloud.init(context, extend);
} catch (Exception e) {
SpiderDebug.log("Cloud init error: " + e.getMessage());
}
ali.init(context, extend);
}
@Override
public String detailContent(List<String> ids) throws Exception {
String url = ids.get(0);
// 使用Cloud类处理各种云盘链接
String cloudResult = cloud.detailContent(ids);
if (cloudResult != null) {
return cloudResult;
}
// 如果不是云盘链接返回普通链接处理结果
return Result.string(vod(url));
if (Ali.pattern.matcher(ids.get(0)).find()) return ali.detailContent(ids);
return Result.string(vod(ids.get(0)));
}
@Override
public String playerContent(String flag, String id, List<String> vipFlags) {
try {
// 使用Cloud类处理云盘链接的播放
String cloudResult = cloud.playerContent(flag, id, vipFlags);
if (cloudResult != null) {
return cloudResult;
}
} catch (Exception e) {
SpiderDebug.log("Cloud playerContent error: " + e.getMessage());
}
// 原有逻辑处理其他类型链接
if (id.startsWith("http") && id.contains("***")) id = id.replace("***", "#");
if (flag.equals("直連")) return Result.get().url(id).subs(getSubs(id)).string();
if (flag.equals("解析")) return Result.get().parse().jx().url(id).string();
if (flag.equals("嗅探")) return Result.get().parse().url(id).string();
if (flag.equals("迅雷")) return Result.get().url(id).string();
return Result.get().url(id).string();
return ali.playerContent(flag, id, vipFlags);
}
private Vod vod(String url) {

View File

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

View File

@ -1,192 +0,0 @@
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

@ -1,75 +0,0 @@
package com.github.catvod.spider
import android.content.Context
import com.github.catvod.bean.Class
import com.github.catvod.bean.Result
import com.github.catvod.bean.Vod
import com.github.catvod.net.OkHttp
import com.github.catvod.utils.Json
import com.github.catvod.utils.Util
import org.jsoup.Jsoup
import org.jsoup.nodes.Document
import java.net.URLEncoder
import java.nio.charset.Charset
/**
* 电影云集
*
* @author lushunming
* @createdate 2024-12-03
*/
class ReBoYingShi : Cloud() {
private val siteUrl = "https://reboys.cn"
private val headerWithCookie: MutableMap<String?, String?>
get() {
val header: MutableMap<String?, String?> = HashMap<String?, String?>()
header.put("User-Agent", Util.CHROME)
return header
}
@Throws(Exception::class)
override fun init(context: Context?, extend: String?) {
super.init(context, extend)
}
@Throws(Exception::class)
override fun searchContent(key: String?, quick: Boolean): String? {
return searchContent(key, "1")
}
@Throws(Exception::class)
override fun searchContent(key: String?, quick: Boolean, pg: String?): String? {
return searchContent(key, pg)
}
private fun searchContent(key: String?, pg: String?): String? {
val searchPageURL = siteUrl + "/s/${URLEncoder.encode(key, Charset.defaultCharset().name())}.html"
val html = OkHttp.string(searchPageURL, this.headerWithCookie)
val apiToken = Util.findByRegex("const apiToken = \"(.*?)\";", html, 1)
val searchURL = siteUrl + "/search?keyword=${URLEncoder.encode(key, Charset.defaultCharset().name())}"
val header = headerWithCookie.toMutableMap()
header.put("API-TOKEN", apiToken)
val json = OkHttp.string(searchURL, header)
val jsonObj = Json.safeObject(json)
var vodList = emptyList<Vod>()
if (jsonObj.get("code").asInt == 0) {
val results = jsonObj.get("data").asJsonObject.get("data").asJsonObject.get("results").asJsonArray
vodList = results.map {
val title = it.asJsonObject.get("title").asString
val vodId = it.asJsonObject.get("links").asJsonArray[0].asJsonObject.get("url").asString
Vod(vodId, title, "", "")
}
}
return Result.string(vodList)
}
}

View File

@ -1,248 +0,0 @@
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

@ -0,0 +1,211 @@
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

@ -1,56 +0,0 @@
package com.github.catvod.spider
import com.github.catvod.bean.Result
import com.github.catvod.bean.Vod
import com.github.catvod.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

@ -1,56 +0,0 @@
package com.github.catvod.spider
import com.github.catvod.bean.Result
import com.github.catvod.bean.Vod
import com.github.catvod.net.OkHttp
import com.github.catvod.utils.Json
import com.github.catvod.utils.Util
import java.net.URLEncoder
import java.nio.charset.Charset
/**
* @author zhixc
*/
class Tg189Search : Cloud() {
private val URL = "https://tgsou.252035.xyz/"
private val header: Map<String, String>
get() {
val header: MutableMap<String, String> = HashMap()
header["User-Agent"] = Util.CHROME
return header
}
@Throws(Exception::class)
override fun searchContent(key: String, quick: Boolean): String {
val url =
URL + "?channelUsername=cloud189_group,yunpan139,cloud189_group,cloudtianyi,kuakeclound,tianyirigeng,txtyzy,tyypzhpd,tyysypzypd,yunpan189&pic=true&keyword=" + URLEncoder.encode(
key, Charset.defaultCharset().name()
)
val list: MutableList<Vod> = ArrayList()
val html = OkHttp.string(url, header)
val json = Json.safeObject(html);
json["results"].asJsonArray.forEach { element ->
val array = element.asString.split("$$$")
if (array.size >= 2 && array[1].isNotEmpty()) {
array[1].split("##").forEach {
val id = it.split("@")[0]
val pic = it.split("@")[1].split("$$")[0]
val name = it.split("@")[1].split("$$")[1]
list.add(Vod(id, name, pic, ""))
}
}
}
return Result.string(list)
}
}

View File

@ -1,57 +0,0 @@
package com.github.catvod.spider
import com.github.catvod.bean.Result
import com.github.catvod.bean.Vod
import com.github.catvod.net.OkHttp
import com.github.catvod.utils.Json
import com.github.catvod.utils.Util
import java.net.URLEncoder
import java.nio.charset.Charset
/**
* @author zhixc
*/
class TgQuarkSearch : Cloud() {
private val URL = "https://tgsou.252035.xyz/"
private val header: Map<String, String>
get() {
val header: MutableMap<String, String> = HashMap()
header["User-Agent"] = Util.CHROME
return header
}
@Throws(Exception::class)
override fun searchContent(key: String, quick: Boolean): String {
val url =
URL + "?channelUsername=alyp_1,clouddriveresources,dianyingshare,hdhhd21,jdjdn1111,leoziyuan,NewQuark,PanjClub,Quark_Movies,xiangxiunb,yunpanchat,yunpanqk,XiangxiuNB,alyp_4K_Movies,alyp_Animation,alyp_TV,alyp_JLP&pic=true&keyword=" + URLEncoder.encode(
key, Charset.defaultCharset().name()
)
val list: MutableList<Vod> = ArrayList()
val html = OkHttp.string(url, header)
val json = Json.safeObject(html);
json["results"].asJsonArray.forEach { element ->
val array = element.asString.split("$$$")
if (array.size >= 2 && array[1].isNotEmpty()) {
array[1].split("##").forEach {
val id = it.split("@")[0]
val pic = it.split("@")[1].split("$$")[0]
val name = it.split("@")[1].split("$$")[1]
list.add(Vod(id, name, pic, ""))
}
}
}
return Result.string(list)
}
}

View File

@ -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.crawler.SpiderDebug;
import com.github.catvod.net.OkHttp;
import com.github.catvod.utils.Json;
import com.github.catvod.utils.Util;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author zhixc
*/
public class TgSearch extends Cloud {
private final String URL = "https://tg.252035.xyz/";
private Map<String, String> getHeader() {
Map<String, String> header = new HashMap<>();
header.put("User-Agent", Util.CHROME);
return header;
}
@Override
public String searchContent(String key, boolean quick) throws Exception {
String url = URL + "?channelUsername=tianyirigeng,tyypzhpd,XiangxiuNB,yunpanpan,kuakeyun,zaihuayun,Quark_Movies,alyp_4K_Movies,vip115hot,yunpanshare,dianyingshare&keyword=" + URLEncoder.encode(key, Charset.defaultCharset().name());
List<Vod> list = new ArrayList<>();
String html = OkHttp.string(url, getHeader());
String[] arr = html.split(":I");
if (arr.length > 0) {
for (String s : arr) {
Document doc = Jsoup.parse(Util.findByRegex("链接(.*)", s, 1));
String id = doc.select(" a").attr("href");
String name = Util.findByRegex("名称(.*)描述", s, 1).replace("","").replace(":","");
String desc = Util.findByRegex("描述(.*) 链接", s, 1).replace("","").replace(":","");
list.add(new Vod(id, name, "", desc));
}
}
return Result.string(list);
}
}

View File

@ -1,47 +0,0 @@
package com.github.catvod.spider
import com.github.catvod.bean.Result
import com.github.catvod.bean.Vod
import com.github.catvod.net.OkHttp
import com.github.catvod.utils.Util
import org.jsoup.Jsoup
import java.net.URLEncoder
import java.nio.charset.Charset
/**
* @author zhixc
*/
class TgSearch : Cloud() {
private val URL = "https://tg.252035.xyz/"
private val header: Map<String, String>
get() {
val header: MutableMap<String, String> = HashMap()
header["User-Agent"] = Util.CHROME
return header
}
@Throws(Exception::class)
override fun searchContent(key: String, quick: Boolean): String {
val url =
URL + "?channelUsername=tianyirigeng,tyypzhpd,XiangxiuNB,yunpanpan,kuakeyun,zaihuayun,Quark_Movies,alyp_4K_Movies,vip115hot,yunpanshare,dianyingshare&pic=true&keyword=" + URLEncoder.encode(
key, Charset.defaultCharset().name()
)
val list: MutableList<Vod> = ArrayList()
val html = OkHttp.string(url, header)
val arr = html.split(":I".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
if (arr.size > 0) {
for (s in arr) {
val doc = Jsoup.parse(s)
val id = doc.select(" a").eachAttr("href")
.first { it.contains("189") || it.contains("139") || it.contains("quark") }
val name = doc.select("strong").text()
list.add(Vod(id, name, "", ""))
}
}
return Result.string(list)
}
}

View File

@ -1,258 +0,0 @@
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

@ -1,258 +0,0 @@
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

@ -1,258 +0,0 @@
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

@ -1,258 +0,0 @@
package com.github.catvod.spider;
import android.content.Context;
import android.text.TextUtils;
import com.github.catvod.bean.Result;
import com.github.catvod.bean.Vod;
import com.github.catvod.net.OkHttp;
import com.github.catvod.net.OkResult;
import com.github.catvod.utils.Json;
import com.github.catvod.utils.Util;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import java.util.*;
public class TgSearchBaidu extends Cloud {
private static final String KEY_API_URLS = "api_urls";
private static final String KEY_DOMAIN_MAP = "siteurl";
private static final String KEY_SOURCES = "sources";
private static final int DEFAULT_PAGE_SIZE = 10;
private List<String> apiUrls = new ArrayList<>();
private Map<String, String> domainMap = new HashMap<String, String>() {{
put("alipan", "阿里");
put("aliyundrive", "阿里");
put("quark", "夸克");
put("115cdn", "115");
put("baidu.com", "百度");
put("uc", "UC");
put("189.cn", "天翼");
put("139.com", "移动");
put("123", "123盘");
}};
private Set<String> sources = new HashSet<>();
private String[] extInfos = null;
@Override
public synchronized void init(Context context, String extend) throws Exception {
super.init(context, extend);
this.apiUrls.clear();
if (!TextUtils.isEmpty(extend)) {
try {
if (extend.contains("###")) {
String[] infos = extend.split("###");
this.extInfos = infos;
if (infos.length > 0 && !TextUtils.isEmpty(infos[0])) {
processExtendConfig(infos[0]);
}
} else {
this.extInfos = new String[]{extend};
processExtendConfig(extend);
}
} catch (Exception e) {
}
}
this.extInfos = null; // 重置避免内存泄漏
}
private void processExtendConfig(String config) {
try {
// 检查是否为HTTP URL
if (isValidUrl(config)) {
if (!this.apiUrls.contains(config)) {
this.apiUrls.add(config);
}
return;
}
// 尝试JSON格式解析
JsonObject ext = Json.safeObject(config);
// 处理API URLs配置
processApiUrls(ext);
// 处理域名映射配置
processDomainMap(ext);
// 处理来源过滤配置
processSources(ext, true);
} catch (Exception e) {
// 可以添加日志记录
}
}
private void processApiUrls(JsonObject config) {
if (config.has(KEY_API_URLS) && config.get(KEY_API_URLS).isJsonArray()) {
JsonArray urlsArray = config.getAsJsonArray(KEY_API_URLS);
for (JsonElement element : urlsArray) {
if (element != null && element.isJsonPrimitive() && element.getAsJsonPrimitive().isString()) {
String url = element.getAsString();
if (isValidUrl(url) && !this.apiUrls.contains(url)) {
this.apiUrls.add(url);
}
}
}
}
}
private void processDomainMap(JsonObject config) {
if (config.has(KEY_DOMAIN_MAP) && config.get(KEY_DOMAIN_MAP).isJsonObject()) {
JsonObject customDomains = config.getAsJsonObject(KEY_DOMAIN_MAP);
for (Map.Entry<String, JsonElement> entry : customDomains.entrySet()) {
if (entry == null || entry.getValue() == null) continue;
String domain = entry.getKey();
String sourceName = "";
try {
sourceName = entry.getValue().getAsString();
} catch (Exception e) {
continue;
}
if (!TextUtils.isEmpty(domain) && !TextUtils.isEmpty(sourceName) && !domainMap.containsKey(domain)) {
domainMap.put(domain, sourceName);
}
}
}
}
private void processSources(JsonObject config, boolean clearExisting) {
if (config.has(KEY_SOURCES) && config.get(KEY_SOURCES).isJsonArray()) {
if (clearExisting) {
this.sources.clear();
}
JsonArray sourcesArray = config.getAsJsonArray(KEY_SOURCES);
for (JsonElement element : sourcesArray) {
if (element != null && element.isJsonPrimitive() && element.getAsJsonPrimitive().isString()) {
String source = element.getAsString();
if (!TextUtils.isEmpty(source) && !sources.contains(source)) {
sources.add(source);
}
}
}
}
}
private boolean isValidUrl(String url) {
return !TextUtils.isEmpty(url) && (url.startsWith("http://") || url.startsWith("https://"));
}
private Map<String, String> getHeader() {
Map<String, String> header = new HashMap<>();
header.put("User-Agent", Util.CHROME);
return header;
}
@Override
public String searchContent(String key, boolean quick) throws Exception {
if (key == null || key.trim().isEmpty()) {
return Result.error("关键词不能为空");
}
return performSearch(key, 1, DEFAULT_PAGE_SIZE);
}
private String performSearch(String key, int page, int pageSize) throws Exception {
List<Vod> list = new ArrayList<>();
int total = 0;
Map<String, String> params = new HashMap<>();
params.put("kw", key);
params.put("page", String.valueOf(page));
params.put("size", String.valueOf(pageSize));
for (String apiUrl : apiUrls) {
if (!isValidUrl(apiUrl)) continue;
try {
OkResult result = OkHttp.get(apiUrl, params, getHeader());
if (result.getCode() == 500 || TextUtils.isEmpty(result.getBody())) continue;
JsonObject jsonObject = Json.safeObject(result.getBody());
if (!jsonObject.has("code") || jsonObject.get("code").getAsInt() != 0 || !jsonObject.has("data"))
continue;
JsonObject data = jsonObject.getAsJsonObject("data");
total = data.has("total") && !data.get("total").isJsonNull() ? data.get("total").getAsInt() : 0;
// 直接检查merged_by_type字段无需三重判断
if (!data.has("merged_by_type") || !data.get("merged_by_type").isJsonObject()) continue;
JsonObject mergedByType = data.getAsJsonObject("merged_by_type");
for (Map.Entry<String, JsonElement> categoryEntry : mergedByType.entrySet()) {
if (!categoryEntry.getValue().isJsonArray()) continue;
JsonArray items = categoryEntry.getValue().getAsJsonArray();
for (JsonElement item : items) {
if (!item.isJsonObject()) continue;
JsonObject entry = item.getAsJsonObject();
String vodUrl = entry.has("url") && !entry.get("url").isJsonNull() ? entry.get("url").getAsString() : "";
if (TextUtils.isEmpty(vodUrl)) continue;
// 获取来源信息并映射
String originalSource = entry.has("source") && !entry.get("source").isJsonNull() ? entry.get("source").getAsString() : "未知来源";
String sourceName = mapSource(vodUrl, originalSource);
// 获取标题
String title = entry.has("note") && !entry.get("note").isJsonNull() ? entry.get("note").getAsString() : "未命名资源";
// 获取图片
String pic = getFirstImage(entry);
// 简化VodPlayBuilder的使用
Vod vod = new Vod(vodUrl, title, pic, sourceName + " (" + originalSource + ")");
// 由于只有一个播放源直接设置播放信息
vod.setVodPlayFrom(sourceName);
vod.setVodPlayUrl("播放源$" + vodUrl);
// 来源过滤
if (sources.isEmpty() || sources.contains(sourceName)) {
list.add(vod);
}
}
}
int pageCount = total > 0 && pageSize > 0 ? (total + pageSize - 1) / pageSize : 1;
return Result.string(page, pageCount, pageSize, total, list);
} catch (Exception e) {
// 出错时继续尝试下一个API
continue;
}
}
return Result.error("无法连接到任何搜索API");
}
// 提取来源映射逻辑为单独方法
private String mapSource(String vodUrl, String originalSource) {
for (Map.Entry<String, String> domainEntry : domainMap.entrySet()) {
if (vodUrl.contains(domainEntry.getKey())) {
return domainEntry.getValue();
}
}
return originalSource;
}
// 提取图片获取逻辑为单独方法
private String getFirstImage(JsonObject entry) {
if (entry.has("images") && !entry.get("images").isJsonNull() && entry.get("images").isJsonArray()) {
JsonArray images = entry.getAsJsonArray("images");
if (images.size() > 0 && !images.get(0).isJsonNull() && images.get(0).isJsonPrimitive()) {
return images.get(0).getAsString();
}
}
return "";
}
}

View File

@ -1,258 +0,0 @@
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

@ -1,56 +0,0 @@
package com.github.catvod.spider
import com.github.catvod.bean.Result
import com.github.catvod.bean.Vod
import com.github.catvod.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) {
public String detailContentVodPlayUrl(List<String> ids) throws Exception {
List<String> playUrl = new ArrayList<>();
for (String id : ids) {
ShareData shareData = TianyiApi.get().getShareData(id, "");
@ -77,7 +77,6 @@ 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,17 +71,11 @@ public class UC extends Spider {
* @param ids share_link 集合
* @return 詳情內容視頻播放地址
*/
public String detailContentVodPlayUrl(List<String> ids) {
public String detailContentVodPlayUrl(List<String> ids) throws Exception {
List<String> playUrl = new ArrayList<>();
for (String id : ids) {
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("");
}
playUrl.add(UCApi.get().getVod(shareData).getVodPlayUrl());
}
return TextUtils.join("$$$", playUrl);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -23,8 +23,6 @@ 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\\/[^\"]+)";
@ -32,8 +30,6 @@ 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");
@ -261,12 +257,6 @@ 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

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

View File

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

View File

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

View File

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

View File

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

View File

@ -28,7 +28,7 @@ public class QuarkTest {
Init.init(mockContext);
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_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");
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");
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/469c2acf8640"));
String content = spider.detailContent(Arrays.asList("https://pan.quark.cn/s/38c5e16d71f7"));
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("quark原画", "a04522f504a742db8ebaf69e3b7f50d6++375807f3f1068a8fdabac127ec4db89f++469c2acf8640++PVTgPNXNtRFDDkE6SAYX4KPSjk9xl449JkTHl6mtu7k=", new ArrayList<>());
String content = spider.playerContent("普画","41ea9a50cbdd4e50b019bcd78687ebc1++22fc6fa8350d22e0eaecc49035368e81++38c5e16d71f7++WFcYTmRhjJpKTui56aleYdzBZi9R203GERBVzYNxDxI=",new ArrayList<>());
System.out.println("playerContent--" + content);
JsonObject map = Json.safeObject(content);
Gson gson = new GsonBuilder().setPrettyPrinting().create();

View File

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

View File

@ -1,100 +0,0 @@
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

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

View File

@ -1,7 +1,9 @@
import android.app.Application;
import com.github.catvod.server.Server;
import com.github.catvod.spider.Init;
import com.github.catvod.spider.TgSearchBaidu;
import com.github.catvod.spider.PanSearch;
import com.github.catvod.spider.TgSearch;
import com.github.catvod.spider.TianYiSo;
import com.github.catvod.utils.Json;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
@ -11,7 +13,6 @@ import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.util.ArrayList;
import java.util.Arrays;
@RunWith(RobolectricTestRunner.class)
@ -19,26 +20,25 @@ public class TgSearchTest {
private Application mockContext;
private TgSearchBaidu spider;
private TgSearch spider;
@org.junit.Before
public void setUp() throws Exception {
mockContext = RuntimeEnvironment.application;
Init.init(mockContext);
spider = new TgSearchBaidu();
spider = new TgSearch();
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,"{\n" +
"\t\"username\":\"18896781601\" ,\"password\":\"Lushunming@0526\"\n" +
"}"); */
spider.init(mockContext, "{\n" + " \"api_urls\": [\n" + " \"https://psweb.banye.tech:7777/api/search\",\n" + " \"https://so.566987.xyz/api/search\",\n" + " \"http://152.69.222.142:8088/api/search\"\n" + " ],\n" + " \"sources\": [\n" + " \"123盘\"\n" + " ]\n" + " }");
spider.init(mockContext, "{\"tianyicookie\":\"{\\\"open.e.189.cn\\\":[{\\\"name\\\":\\\"SSON\\\",\\\"value\\\":\\\"dc466c8192e3109eaea837c1d136c1fd065253ce1c7d3a66ca1520d7d6d6307b10a1fe65c7becac73b95f24a6e681e654ec4f47c39533ebcc48bb78d6d6e63d1bbf3334e6e97eaa7092d34f87bf1209ee35f344871bc5a329eac34ae948d399d4a6b3b28a929c4f353ade0981657e9e0f09ce27cc1c15d8322c6e45a8ebb21eb431509f1dd7dc3a7856b32b0991d654d5ced73dd20b764ca8737600cbe699c37ccf59b3c610893fc42bdc08b477c5d394e290c14d175d1ca0ee9fa61a1a8dcac7007e9219fd0ae6ccd5dc760524213f85b6b8c6166af01a31336dab797d9118010b81a5a3c26e08e\\\",\\\"expiresAt\\\":253402300799999,\\\"domain\\\":\\\"e.189.cn\\\",\\\"path\\\":\\\"/\\\",\\\"secure\\\":true,\\\"httpOnly\\\":true,\\\"persistent\\\":true,\\\"hostOnly\\\":false},{\\\"name\\\":\\\"GUID\\\",\\\"value\\\":\\\"525d8874e53e46a7ba3ed8907e9fef1f\\\",\\\"expiresAt\\\":1775176321000,\\\"domain\\\":\\\"e.189.cn\\\",\\\"path\\\":\\\"/\\\",\\\"secure\\\":false,\\\"httpOnly\\\":false,\\\"persistent\\\":true,\\\"hostOnly\\\":false},{\\\"name\\\":\\\"pageOp\\\",\\\"value\\\":\\\"336b9ddc820212fa6c9b5a0cfd7bf5b3\\\",\\\"expiresAt\\\":253402300799999,\\\"domain\\\":\\\"e.189.cn\\\",\\\"path\\\":\\\"/\\\",\\\"secure\\\":false,\\\"httpOnly\\\":false,\\\"persistent\\\":false,\\\"hostOnly\\\":false},{\\\"name\\\":\\\"OPENINFO\\\",\\\"value\\\":\\\"33c28688ef52ce9e3a9ef87388047efbde5e3e2e4c7ef6ef267632468c7dfaf294ff59fa59d34801\\\",\\\"expiresAt\\\":253402300799999,\\\"domain\\\":\\\"e.189.cn\\\",\\\"path\\\":\\\"/\\\",\\\"secure\\\":false,\\\"httpOnly\\\":true,\\\"persistent\\\":false,\\\"hostOnly\\\":false},{\\\"name\\\":\\\"GRAYNUMBER\\\",\\\"value\\\":\\\"319DE3F68C8730862F3BEF66F3D635B7\\\",\\\"expiresAt\\\":1775177653000,\\\"domain\\\":\\\"e.189.cn\\\",\\\"path\\\":\\\"/\\\",\\\"secure\\\":false,\\\"httpOnly\\\":false,\\\"persistent\\\":true,\\\"hostOnly\\\":false}],\\\"cloud.189.cn\\\":[{\\\"name\\\":\\\"JSESSIONID\\\",\\\"value\\\":\\\"431787526C43DF21B6373E914FE597EC\\\",\\\"expiresAt\\\":253402300799999,\\\"domain\\\":\\\"cloud.189.cn\\\",\\\"path\\\":\\\"/\\\",\\\"secure\\\":false,\\\"httpOnly\\\":true,\\\"persistent\\\":false,\\\"hostOnly\\\":true},{\\\"name\\\":\\\"COOKIE_LOGIN_USER\\\",\\\"value\\\":\\\"0C7407F59A6E5896EB6B777056E160DB020BAE67B121B5930CCD4777073744055308F7E8CD03F2FC2399E4823F60ECDD74120CEE4C529017\\\",\\\"expiresAt\\\":253402300799999,\\\"domain\\\":\\\"cloud.189.cn\\\",\\\"path\\\":\\\"/\\\",\\\"secure\\\":false,\\\"httpOnly\\\":true,\\\"persistent\\\":false,\\\"hostOnly\\\":false}]}\"}");
// spider.init(mockContext, "");
}
@org.junit.Test
public void searchContent() throws Exception {
String content = spider.searchContent("水饺皇后", false);
String content = spider.searchContent("红海行动", false);
JsonObject map = Json.safeObject(content);
Gson gson = new GsonBuilder().setPrettyPrinting().create();
System.out.println("searchContent--" + gson.toJson(map));
@ -48,18 +48,7 @@ public class TgSearchTest {
@org.junit.Test
public void detailContent() throws Exception {
String content = spider.detailContent(Arrays.asList("https://123684.com/s/u9izjv-smUWv"));
JsonObject map = Json.safeObject(content);
Gson gson = new GsonBuilder().setPrettyPrinting().create();
System.out.println("detailContent--" + gson.toJson(map));
Assert.assertFalse(map.getAsJsonArray("list").isEmpty());
}
@org.junit.Test
public void playerContent() throws Exception {
String content1 = spider.detailContent(Arrays.asList("https://123684.com/s/u9izjv-smUWv"));
String content = spider.playerContent("pan123原画", "eyJmaWxlbmFtZSI6IlRoZS5EdW1wbGluZy5RdWVlbi4yMDI1LjEwODBwLldFQi1ETC5IMjY0LkFBQy5tcDQiLCJzaGFyZUtleSI6InU5aXpqdi1zbVVXdiIsInNoYXJlUHdkIjoiIiwibmV4dCI6LTEsImZpbGVJZCI6MTg1NjgwODEsIlMzS2V5RmxhZyI6IjE4NDMwNTU4NTItMCIsIlNpemUiOjY0MDQyNTYzMTIsIkV0YWciOiIwYjNjZGIyOTYxZWM2NmQ5MjAyMTViOTRmZGY2MDZjNyJ9", new ArrayList<>());
String content = spider.detailContent(Arrays.asList("/s/LEvn4lUGB6ufdQ"));
JsonObject map = Json.safeObject(content);
Gson gson = new GsonBuilder().setPrettyPrinting().create();
System.out.println("detailContent--" + gson.toJson(map));

View File

@ -28,9 +28,8 @@ public class TianYiSoTest {
spider = new TianYiSo();
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,"{\n" +
"\t\"username\":\"18896781601\" ,\"password\":\"Lushunming@0526\"\n" +
"}"); // spider.init(mockContext, "");
spider.init(mockContext, "{\"tianyicookie\":\"{\\\"open.e.189.cn\\\":[{\\\"name\\\":\\\"SSON\\\",\\\"value\\\":\\\"dc466c8192e3109eaea837c1d136c1fd065253ce1c7d3a66ca1520d7d6d6307b10a1fe65c7becac73b95f24a6e681e654ec4f47c39533ebcc48bb78d6d6e63d1bbf3334e6e97eaa7092d34f87bf1209ee35f344871bc5a329eac34ae948d399d4a6b3b28a929c4f353ade0981657e9e0f09ce27cc1c15d8322c6e45a8ebb21eb431509f1dd7dc3a7856b32b0991d654d5ced73dd20b764ca8737600cbe699c37ccf59b3c610893fc42bdc08b477c5d394e290c14d175d1ca0ee9fa61a1a8dcac7007e9219fd0ae6ccd5dc760524213f85b6b8c6166af01a31336dab797d9118010b81a5a3c26e08e\\\",\\\"expiresAt\\\":253402300799999,\\\"domain\\\":\\\"e.189.cn\\\",\\\"path\\\":\\\"/\\\",\\\"secure\\\":true,\\\"httpOnly\\\":true,\\\"persistent\\\":true,\\\"hostOnly\\\":false},{\\\"name\\\":\\\"GUID\\\",\\\"value\\\":\\\"525d8874e53e46a7ba3ed8907e9fef1f\\\",\\\"expiresAt\\\":1775176321000,\\\"domain\\\":\\\"e.189.cn\\\",\\\"path\\\":\\\"/\\\",\\\"secure\\\":false,\\\"httpOnly\\\":false,\\\"persistent\\\":true,\\\"hostOnly\\\":false},{\\\"name\\\":\\\"pageOp\\\",\\\"value\\\":\\\"336b9ddc820212fa6c9b5a0cfd7bf5b3\\\",\\\"expiresAt\\\":253402300799999,\\\"domain\\\":\\\"e.189.cn\\\",\\\"path\\\":\\\"/\\\",\\\"secure\\\":false,\\\"httpOnly\\\":false,\\\"persistent\\\":false,\\\"hostOnly\\\":false},{\\\"name\\\":\\\"OPENINFO\\\",\\\"value\\\":\\\"33c28688ef52ce9e3a9ef87388047efbde5e3e2e4c7ef6ef267632468c7dfaf294ff59fa59d34801\\\",\\\"expiresAt\\\":253402300799999,\\\"domain\\\":\\\"e.189.cn\\\",\\\"path\\\":\\\"/\\\",\\\"secure\\\":false,\\\"httpOnly\\\":true,\\\"persistent\\\":false,\\\"hostOnly\\\":false},{\\\"name\\\":\\\"GRAYNUMBER\\\",\\\"value\\\":\\\"319DE3F68C8730862F3BEF66F3D635B7\\\",\\\"expiresAt\\\":1775177653000,\\\"domain\\\":\\\"e.189.cn\\\",\\\"path\\\":\\\"/\\\",\\\"secure\\\":false,\\\"httpOnly\\\":false,\\\"persistent\\\":true,\\\"hostOnly\\\":false}],\\\"cloud.189.cn\\\":[{\\\"name\\\":\\\"JSESSIONID\\\",\\\"value\\\":\\\"431787526C43DF21B6373E914FE597EC\\\",\\\"expiresAt\\\":253402300799999,\\\"domain\\\":\\\"cloud.189.cn\\\",\\\"path\\\":\\\"/\\\",\\\"secure\\\":false,\\\"httpOnly\\\":true,\\\"persistent\\\":false,\\\"hostOnly\\\":true},{\\\"name\\\":\\\"COOKIE_LOGIN_USER\\\",\\\"value\\\":\\\"0C7407F59A6E5896EB6B777056E160DB020BAE67B121B5930CCD4777073744055308F7E8CD03F2FC2399E4823F60ECDD74120CEE4C529017\\\",\\\"expiresAt\\\":253402300799999,\\\"domain\\\":\\\"cloud.189.cn\\\",\\\"path\\\":\\\"/\\\",\\\"secure\\\":false,\\\"httpOnly\\\":true,\\\"persistent\\\":false,\\\"hostOnly\\\":false}]}\"}");
// spider.init(mockContext, "");
}

View File

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

View File

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

View File

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

View File

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

View File

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

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("http://cloud.189.cn/t/3uIFJrzIFJV3访问码qf4b", "");
com.github.catvod.bean.tianyi.ShareData shareData1 = TianyiApi.get().getShareData("https://cloud.189.cn/web/share?code=2eyARfBzURZj访问码kz6y", "");
// TianyiApi.get().getVod(shareData1);
ShareData shareData2 = TianyiApi.get().getShareData("https://cloud.189.cn/t/ZvEjUvq6FNr2", "");

View File

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

View File

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

View File

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

BIN
jar/GM.jar Normal file

Binary file not shown.

7668
jar/GM.jobf Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -1 +1 @@
5cca7cdb874fb79dee68e08a876fc8ec
4a9814b359e0f39cf4613cd801871c06

View File

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

View File

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

View File

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