diff --git a/app/src/main/java/com/github/catvod/api/BaiDuYunHandler.kt b/app/src/main/java/com/github/catvod/api/BaiDuYunHandler.kt index d8d7d9bf..75bbd6fe 100644 --- a/app/src/main/java/com/github/catvod/api/BaiDuYunHandler.kt +++ b/app/src/main/java/com/github/catvod/api/BaiDuYunHandler.kt @@ -1,37 +1,256 @@ -package com.github.catvod.api; +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/" + ) -import com.github.catvod.bean.BD.Cache; -import com.github.catvod.bean.yun.User; -import com.github.catvod.utils.Path; - -import java.io.File; - -public class BaiDuYunHandler { - - private final Cache cache; - - public File getCache() { - return Path.tv("bd"); + fun getCache(): File { + return Path.tv("bd") } - private BaiDuYunHandler() { - cache = Cache.objectFrom(Path.read(getCache())); + init { + cache = Cache.objectFrom(Path.read(getCache())) } - private static class Loader { - static volatile BaiDuYunHandler INSTANCE = new BaiDuYunHandler(); + private object Loader { + @Volatile + var INSTANCE: BaiDuYunHandler = BaiDuYunHandler() } - public static BaiDuYunHandler get() { - return BaiDuYunHandler.Loader.INSTANCE; + 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 = HashMap() + headers.put( + "user-agent", + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36" + ) + + + 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 { + 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 { + 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"]) + cache.setUser(User.objectFrom(this.cookies)) + + //停止检验线程,关闭弹窗 + stopService() + 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 { + if (cookies == null || cookies.isEmpty()) { + return "" + } + + val cookieList: MutableList = ArrayList() + 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) { + } } - public String getToken() { - User user = cache.getUser(); - return user.getCookie(); - //return "cGM6MTg4OTY3ODE2MDE6eTM1Tjd1dG58MXxSQ1N8MTc1NDQ2OTgwNzEyOXxzMlN0T1VEV3lOVmF5V3pNbGFfM2tJbVp1ZmlqSHBqaEhTSzVyNHZqVXNRLmlhV3loSUxHNDFkMUI5N1BqXzhWN0dtVWtKLnBTclhpNGpZU1EuTGZWMTV3MVFoZmNpcEVoZkxUV2tvYjB0bkFTYV9RTUhhaHhveWx6YkdmcEhQdjNCS1lrbnp1LkxaWDdKOE40YkNNRjkzT3piNmx2Y0d3TWdVUkl5b18ubVUt"; + 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) } } \ No newline at end of file diff --git a/app/src/main/java/com/github/catvod/api/BaiduDrive.kt b/app/src/main/java/com/github/catvod/api/BaiduDrive.kt index a4ea78bf..caf63595 100644 --- a/app/src/main/java/com/github/catvod/api/BaiduDrive.kt +++ b/app/src/main/java/com/github/catvod/api/BaiduDrive.kt @@ -22,6 +22,7 @@ object BaiduDrive { "Referer" to "https://pan.baidu.com/" ) + private val saveDirName = "TVBOX_BD" private var cookies = BaiDuYunHandler.get().token @@ -37,6 +38,11 @@ object BaiduDrive { } fun processShareLinks(urls: List): Pair, List> { + //首先确保cookie不为空 + if (cookies.isEmpty()) { + BaiDuYunHandler.get().startScan() + cookies = BaiDuYunHandler.get().token + } if (urls.isEmpty()) return emptyList() to emptyList() @@ -314,6 +320,7 @@ object BaiduDrive { } + private fun parseQueryParams(url: String): Map> { val query = url.substringAfter( "?" @@ -768,7 +775,7 @@ object BaiduDrive { fun playerContent(json: JsonObject, flag: String): String { val play = getVideoUrl(json, flag); val header = play["header"] as Map - return Result.get().url(buildProxyUrl(play["url"] as String, header) ).octet().header(header).string(); + return Result.get().url(buildProxyUrl(play["url"] as String, header)).octet().header(header).string(); } fun getPlayFormatList(): Array { diff --git a/app/src/main/java/com/github/catvod/bean/BD/Cache.java b/app/src/main/java/com/github/catvod/bean/BD/Cache.java index 37211b38..03dca684 100644 --- a/app/src/main/java/com/github/catvod/bean/BD/Cache.java +++ b/app/src/main/java/com/github/catvod/bean/BD/Cache.java @@ -2,7 +2,6 @@ package com.github.catvod.bean.BD; import com.github.catvod.api.BaiDuYunHandler; -import com.github.catvod.api.YunTokenHandler; import com.github.catvod.bean.yun.User; import com.github.catvod.spider.Init; import com.github.catvod.utils.Path; diff --git a/app/src/test/java/com/github/catvod/api/BaiDuYunHandlerTest.java b/app/src/test/java/com/github/catvod/api/BaiDuYunHandlerTest.java new file mode 100644 index 00000000..37f5d13e --- /dev/null +++ b/app/src/test/java/com/github/catvod/api/BaiDuYunHandlerTest.java @@ -0,0 +1,87 @@ +package com.github.catvod.api; + +import cn.hutool.core.io.FileUtil; +import com.github.catvod.utils.Json; +import com.google.gson.JsonObject; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; + +import java.net.URLEncoder; + +@RunWith(RobolectricTestRunner.class) +public class BaiDuYunHandlerTest { + + private BaiDuYunHandler baiDuYunHandler; + + + @Before + public void setUp() { + baiDuYunHandler = BaiDuYunHandler.get(); + + } + + @Test + public void startScan() throws Exception { + // Mock the OkHttp.get method to return a predefined OkResult + // Execute the method under test + FileUtil.writeBytes(baiDuYunHandler.startScan(), "c://qrcode.png"); + + while (true) { + + } + + } + + /* @Test + public void refreshCookie() throws Exception { + + JsonObject obj = Json.safeObject("{\"open.e.189.cn\":{\"OPENINFO\":\"33c28688ef52ce9e3a9ef87388047efbde5e3e2e4c7ef6ef267632468c7dfaf294ff59fa59d34801\",\"pageOp\":\"f73420158c5c010491f1faa4fc91870e\",\"LT\":\"a8900fc0ecae0c59\",\"GUID\":\"b959026ffdf84080ae8567afd9ea4c32\",\"SSON\":\"dc466c8192e3109eaea837c1d136c1fd065253ce1c7d3a66ca1520d7d6d6307b10a1fe65c7becac73b95f24a6e681e654ec4f47c39533ebcc48bb78d6d6e63d1bbf3334e6e97eaa7092d34f87bf1209e256cd4822db68da051a0aeb532d94408c8e50486347fc713813dafc5776a7cfa665ddf96837151232745aa2957fb441d8a79ca7d86f46452060794e6f4b5873ab99ed476629aed2c7b36a44613c92f925dcfd221fce142cd1ecaab667236df697ece293e3ca24030918e5b357bc193118772278748606ade7262bf25ae7527d3c8a059bd48fc08b53b182e61e543a7e9bd1562b50bf80438\"},\"cloud.189.cn\":{\"JSESSIONID\":\"12088774C4B78E632EB944ECA2E6705F\",\"COOKIE_LOGIN_USER\":\"24DA4CBA27A8388982710C2F3D55EFAA84AEE67E9B3EF1B7AC1C565BEEF24C562052CB9B5EAC85E733C10C2704225133CD625407C352ED5D\"}}"); + baiDuYunHandler.setCookie(obj); + baiDuYunHandler.refreshCookie(); + + while (true) { + + } + + } + + @Test + public void download() throws Exception { + // Mock the OkHttp.get method to return a predefined OkResult + // Execute the method under test + + + } + + @Test + public void testgetUUID() throws Exception { + JsonObject uuid = tianYiHandler.getUUID(); + System.out.println(uuid); + } + + @Test + public void testdownloadQRCode() throws Exception { +*//* + JsonObject uuidInfo = tianYiHandler.getUUID(); + String uuid = uuidInfo.get("uuid").getAsString(); + byte[] qrCode = tianYiHandler.downloadQRCode(uuid); + FileUtil.writeBytes(qrCode, "c://qrcode.png"); + + System.out.println(uuid);*//* + + String url = "https://cloud.189.cn/api/portal/callbackUnify.action?browserId=dff95dced0b03d9d972d920f03ddd05e&redirectURL=https%3A%2F%2Fcloud.189.cn%2Fweb%2Fredirect.html"; + + String encode = "https%3A%2F%2Fcloud.189.cn%2Fapi%2Fportal%2FcallbackUnify.action%3FbrowserId%3Ddff95dced0b03d9d972d920f03ddd05e%26redirectURL%3Dhttps%253A%252F%252Fcloud.189.cn%252Fweb%252Fredirect.html"; + assert URLEncoder.encode(url, "UTF-8").equals(encode); + } + + + @Test + public void loginWithPassword() throws Exception { + tianYiHandler.loginWithPassword("18896781601","Lushunming@0526"); + System.out.println("1111"); + } +*/ +} \ No newline at end of file diff --git a/jar/custom_spider.jar b/jar/custom_spider.jar index 190a31d1..06d23b6f 100644 Binary files a/jar/custom_spider.jar and b/jar/custom_spider.jar differ diff --git a/jar/custom_spider.jar.md5 b/jar/custom_spider.jar.md5 index 853e094d..076190ad 100644 --- a/jar/custom_spider.jar.md5 +++ b/jar/custom_spider.jar.md5 @@ -1 +1 @@ -061c059a302046f02caa7543ad2b5ad4 +4f3072169892a8d61428ab147dea4766 diff --git a/json/index.json b/json/index.json index 2d9ab974..c84522a7 100644 --- a/json/index.json +++ b/json/index.json @@ -1,5 +1,5 @@ { - "spider": "https://andoridspidermt.netlify.app/jar/custom_spider.jar;md5;061c059a302046f02caa7543ad2b5ad4", + "spider": "https://andoridspidermt.netlify.app/jar/custom_spider.jar;md5;4f3072169892a8d61428ab147dea4766", "lives": [ { "name": "电视直播",